//此源码被清华学神尹成大魔王专业翻译分析并修改
//尹成QQ77025077
//尹成微信18510341407
//尹成所在QQ群721929980
//尹成邮箱 yinc13@mails.tsinghua.edu.cn
//尹成毕业于清华大学,微软区块链领域全球最有价值专家
//https://mvp.microsoft.com/zh-cn/PublicProfile/4033620
//————————————————————————————————————————————————————————————————————————————————————————————————————————————————
/*
    此文件是Rippled的一部分：https://github.com/ripple/rippled
    版权所有（c）2012，2013 Ripple Labs Inc.

    使用、复制、修改和/或分发本软件的权限
    特此授予免费或不收费的目的，前提是
    版权声明和本许可声明出现在所有副本中。

    本软件按“原样”提供，作者不作任何保证。
    关于本软件，包括
    适销性和适用性。在任何情况下，作者都不对
    任何特殊、直接、间接或后果性损害或任何损害
    因使用、数据或利润损失而导致的任何情况，无论是在
    合同行为、疏忽或其他侵权行为
    或与本软件的使用或性能有关。
**/

//==============================================================

#include <ripple/app/paths/impl/Steps.h>
#include <ripple/app/paths/Credit.h>
#include <ripple/app/paths/NodeDirectory.h>
#include <ripple/app/tx/impl/OfferStream.h>
#include <ripple/basics/contract.h>
#include <ripple/basics/Log.h>
#include <ripple/ledger/Directory.h>
#include <ripple/ledger/PaymentSandbox.h>
#include <ripple/protocol/Book.h>
#include <ripple/protocol/Feature.h>
#include <ripple/protocol/IOUAmount.h>
#include <ripple/protocol/Quality.h>
#include <ripple/protocol/XRPAmount.h>

#include <boost/container/flat_set.hpp>

#include <numeric>
#include <sstream>

namespace ripple {

template<class TIn, class TOut, class TDerived>
class BookStep : public StepImp<TIn, TOut, BookStep<TIn, TOut, TDerived>>
{
protected:
    uint32_t const maxOffersToConsume_;
    Book book_;
    AccountID strandSrc_;
    AccountID strandDst_;
//上一步赎回时收取过户费
    Step const* const prevStep_ = nullptr;
    bool const ownerPaysTransferFee_;
//如果消费了太多的产品，请标记为不活动（干燥）
    bool inactive_ = false;
    beast::Journal j_;

    struct Cache
    {
        TIn in;
        TOut out;

        Cache (TIn const& in_, TOut const& out_)
            : in (in_), out (out_)
        {
        }
    };

    boost::optional<Cache> cache_;

    static
    uint32_t getMaxOffersToConsume(StrandContext const& ctx)
    {
        if (ctx.view.rules().enabled(fix1515))
            return 1000;
        return 2000;
    }

public:
    BookStep (StrandContext const& ctx,
        Issue const& in,
        Issue const& out)
        : maxOffersToConsume_ (getMaxOffersToConsume(ctx))
        , book_ (in, out)
        , strandSrc_ (ctx.strandSrc)
        , strandDst_ (ctx.strandDst)
        , prevStep_ (ctx.prevStep)
        , ownerPaysTransferFee_ (ctx.ownerPaysTransferFee)
        , j_ (ctx.j)
    {
    }

    Book const& book() const
    {
        return book_;
    }

    boost::optional<EitherAmount>
    cachedIn () const override
    {
        if (!cache_)
            return boost::none;
        return EitherAmount (cache_->in);
    }

    boost::optional<EitherAmount>
    cachedOut () const override
    {
        if (!cache_)
            return boost::none;
        return EitherAmount (cache_->out);
    }

    bool
    redeems (ReadView const& sb, bool fwd) const override
    {
        return !ownerPaysTransferFee_;
    }

    boost::optional<Book>
    bookStepBook () const override
    {
        return book_;
    }

    boost::optional<Quality>
    qualityUpperBound(ReadView const& v, bool& redeems) const override;

    std::pair<TIn, TOut>
    revImp (
        PaymentSandbox& sb,
        ApplyView& afView,
        boost::container::flat_set<uint256>& ofrsToRm,
        TOut const& out);

    std::pair<TIn, TOut>
    fwdImp (
        PaymentSandbox& sb,
        ApplyView& afView,
        boost::container::flat_set<uint256>& ofrsToRm,
        TIn const& in);

    std::pair<bool, EitherAmount>
    validFwd (
        PaymentSandbox& sb,
        ApplyView& afView,
        EitherAmount const& in) override;

//检查冻结约束是否有错误。
    TER check(StrandContext const& ctx) const;

    bool inactive() const override {
        return inactive_;
    }

protected:
    std::string logStringImpl (char const* name) const
    {
        std::ostringstream ostr;
        ostr <<
            name << ": " <<
            "\ninIss: " << book_.in.account <<
            "\noutIss: " << book_.out.account <<
            "\ninCur: " << book_.in.currency <<
            "\noutCur: " << book_.out.currency;
        return ostr.str ();
    }

private:
    friend bool operator==(BookStep const& lhs, BookStep const& rhs)
    {
        return lhs.book_ == rhs.book_;
    }

    friend bool operator!=(BookStep const& lhs, BookStep const& rhs)
    {
        return ! (lhs == rhs);
    }

    bool equal (Step const& rhs) const override;

//在一本书中以最佳的质量反复阅读这些产品。
//未提供资金的报价和不好的报价被跳过（并被退回）。
//回电是通过提供SLE来调用的，接受方付费，接受方付费。
//如果回调返回false，则不再处理任何报价。
//返回未提供资金和坏报价以及消耗的报价数量。
    template <class Callback>
    std::pair<boost::container::flat_set<uint256>, std::uint32_t>
    forEachOffer (
        PaymentSandbox& sb,
        ApplyView& afView,
        bool prevStepRedeems,
        Callback& callback) const;

    void consumeOffer (PaymentSandbox& sb,
        TOffer<TIn, TOut>& offer,
        TAmounts<TIn, TOut> const& ofrAmt,
        TAmounts<TIn, TOut> const& stepAmt,
        TOut const& ownerGives) const;
};

//————————————————————————————————————————————————————————————————————————————————————————————————————————————————

//在两种不同的情况下，流动用于转移资金：
//付款，以及
//o提供交叉路口。
//在这两种情况下，处理资金的规则几乎是，但不是
//完全一样。

//付款bookstep模板类（不提供交叉）。
template<class TIn, class TOut>
class BookPaymentStep
    : public BookStep<TIn, TOut, BookPaymentStep<TIn, TOut>>
{
public:
    explicit BookPaymentStep() = default;

    using BookStep<TIn, TOut, BookPaymentStep<TIn, TOut>>::BookStep;
    using BookStep<TIn, TOut, BookPaymentStep<TIn, TOut>>::qualityUpperBound;

//不要限制付款的自交叉质量。
    bool limitSelfCrossQuality (AccountID const&, AccountID const&,
        TOffer<TIn, TOut> const& offer, boost::optional<Quality>&,
        FlowOfferStream<TIn, TOut>&, bool) const
    {
        return false;
    }

//付款可以查看任何质量的报价。
    bool checkQualityThreshold(TOffer<TIn, TOut> const& offer) const
    {
        return true;
    }

//因为RinRate的付款总是和Trin一样的。
    std::uint32_t getOfrInRate (
        Step const*, TOffer<TIn, TOut> const&, std::uint32_t trIn) const
    {
        return trIn;
    }

//因为Routrate的付款总是和Trout一样的。
    std::uint32_t getOfrOutRate (Step const*, TOffer<TIn, TOut> const&,
        AccountID const&, std::uint32_t trOut) const
    {
        return trOut;
    }

    Quality
    qualityUpperBound(ReadView const& v,
        Quality const& ofrQ,
        bool prevStepRedeems) const
    {
//向发盘人收费，而不是向发盘人收费
//即使所有者与发行人相同，也要收取费用。
//（旧代码不收费）
//计算到接受者的金额和收取的金额
//发盘人
        auto rate = [&](AccountID const& id) {
            if (isXRP(id) || id == this->strandDst_)
                return parityRate;
            return transferRate(v, id);
        };

        auto const trIn =
            prevStepRedeems ? rate(this->book_.in.account) : parityRate;
//始终收取转让费，即使所有人是发行人
        auto const trOut =
            this->ownerPaysTransferFee_
            ? rate(this->book_.out.account)
            : parityRate;

        Quality const q1{getRate(STAmount(trOut.value), STAmount(trIn.value))};
        return composed_quality(q1, ofrQ);
    }

    std::string logString () const override
    {
        return this->logStringImpl ("BookPaymentStep");
    }
};

//提供交叉bookstep模板类（不是付款）。
template<class TIn, class TOut>
class BookOfferCrossingStep
    : public BookStep<TIn, TOut, BookOfferCrossingStep<TIn, TOut>>
{
    using BookStep<TIn, TOut, BookOfferCrossingStep<TIn, TOut>>::qualityUpperBound;
private:
//如果可选参数传递给构造函数，则引发的helper函数
//一个也没有。
    static Quality getQuality (boost::optional<Quality> const& limitQuality)
    {
//如果质量不好，那真是一个编程错误。
        assert (limitQuality);
        if (!limitQuality)
            Throw<FlowException> (tefINTERNAL, "Offer requires quality.");
        return *limitQuality;
    }

public:
    BookOfferCrossingStep (
        StrandContext const& ctx, Issue const& in, Issue const& out)
    : BookStep<TIn, TOut, BookOfferCrossingStep<TIn, TOut>> (ctx, in, out)
    , defaultPath_ (ctx.isDefaultPath)
    , qualityThreshold_ (getQuality (ctx.limitQuality))
    {
    }

    bool limitSelfCrossQuality (AccountID const& strandSrc,
        AccountID const& strandDst, TOffer<TIn, TOut> const& offer,
        boost::optional<Quality>& ofrQ, FlowOfferStream<TIn, TOut>& offers,
        bool const offerAttempted) const
    {
//这个方法支持一些正确但有点令人吃惊的方法
//报价交叉行为。情景：
//
//o Alice已经创建了一个或多个报价。
//o Alice创建了另一个可以直接交叉（而不是
//由她以前创建的一个或多个报价自动完成。
//
//交叉报价有什么作用？
//
//o报价交叉口可以继续进行，交叉报价离开。
//一个减价（部分交叉）或零报价
//（精确交叉）在分类帐中。我们不这样做。而且，真的，
//报价创建者可能不想让我们这么做。
//
//o我们可以跳过书中的自我介绍，只需交叉
//不是我们自己的报价。这很有道理，
//但我们不这么做。部分理由是我们只能
//操作订单的顶部。我们不能留下报价
//在后面——它会坐在顶端，阻止其他人进入。
//提供。
//
//o我们可以从
//预订并继续报价交叉。我们就是这么做的。
//
//为了支持这个场景，提供交叉有一个特殊的规则。如果：
//A.我们提供使用默认路径的交叉（无自动桥接），以及
//B.报盘的质量至少和我们的质量一样好，而且
//C.那我们就要越过自己的报价了。
//d.从分类帐中删除旧报价。
        if (defaultPath_ && offer.quality() >= qualityThreshold_ &&
            strandSrc == offer.owner() && strandDst == offer.owner())
        {
//即使没有交叉，也要取消此报价。
            offers.permRmOffer (offer.key());

//如果还没有尝试过提供服务，那么可以转到
//不同的品质。
            if (!offerAttempted)
                ofrQ = boost::none;

//返回true，以便删除当前报价。
            return true;
        }
        return false;
    }

//offer crossing可以用
//质量阈值。
    bool checkQualityThreshold(TOffer<TIn, TOut> const& offer) const
    {
        return !defaultPath_ || offer.quality() >= qualityThreshold_;
    }

//如果艾丽丝付钱给艾丽丝，交叉报价不付过户费。
//定期（非报价交叉）付款不适用此规则。
    std::uint32_t getOfrInRate (Step const* prevStep,
        TOffer<TIn, TOut> const& offer, std::uint32_t trIn) const
    {
        auto const srcAcct = prevStep ?
            prevStep->directStepSrcAcct() :
            boost::none;

return                                        //如果报价交叉
srcAcct &&                                //上一步是直接的（&&P）
offer.owner() == *srcAcct                 //&&SRC是报价所有者
? QUALITY_ONE : trIn;                     //那么比率=质量
    }

//请参见GetOfrinRate（）上的注释。
    std::uint32_t getOfrOutRate (
        Step const* prevStep, TOffer<TIn, TOut> const& offer,
        AccountID const& strandDst, std::uint32_t trOut) const
    {
return                                        //如果报价交叉
prevStep && prevStep->bookStepBook() &&   //上一步是bookstep（&&P）
offer.owner() == strandDst                //&&dest是报价所有者
? QUALITY_ONE : trOut;                    //那么比率=质量
    }

    Quality
    qualityUpperBound(ReadView const& v,
        Quality const& ofrQ,
        bool prevStepRedeems) const
    {
//优惠X-ing不收取转让费，当优惠的所有者
//与链DST相同。“质量上界”很重要
//是质量的上界（用于忽略股）
//其质量不能达到最低阈值）。计算时
//质量假设不收取费用，否则估计将不再
//做一个上界。
        return ofrQ;
    }

    std::string logString () const override
    {
        return this->logStringImpl ("BookOfferCrossingStep");
    }

private:
    bool const defaultPath_;
    Quality const qualityThreshold_;
};

//————————————————————————————————————————————————————————————————————————————————————————————————————————————————

template <class TIn, class TOut, class TDerived>
bool BookStep<TIn, TOut, TDerived>::equal (Step const& rhs) const
{
    if (auto bs = dynamic_cast<BookStep<TIn, TOut, TDerived> const*>(&rhs))
        return book_ == bs->book_;
    return false;
}

template <class TIn, class TOut, class TDerived>
boost::optional<Quality>
BookStep<TIn, TOut, TDerived>::qualityUpperBound(
    ReadView const& v, bool& redeems) const
{
    auto const prevStepRedeems = redeems;
    redeems = this->redeems(v, true);

//如果目录从不为空，则可以简化（并加快速度）。
    Sandbox sb(&v, tapNONE);
    BookTip bt(sb, book_);
    if (!bt.step(j_))
        return boost::none;

    return static_cast<TDerived const*>(this)->qualityUpperBound(
        v, bt.quality(), prevStepRedeems);
}

//根据给定的输入限制调整报价金额和步进金额
template <class TIn, class TOut>
static
void limitStepIn (Quality const& ofrQ,
    TAmounts<TIn, TOut>& ofrAmt,
    TAmounts<TIn, TOut>& stpAmt,
    TOut& ownerGives,
    std::uint32_t transferRateIn,
    std::uint32_t transferRateOut,
    TIn const& limit)
{
    if (limit < stpAmt.in)
    {
        stpAmt.in = limit;
        auto const inLmt = mulRatio (
            /*amt.in，quality_one，transferratein，/*roundup*/false）；
        oframt=ofrq.ceil_in（oframt，inlmt）；
        stpamt.out=oframt.out；
        所有者给出=多重比率（
            oframt.out、transferrateout、quality_one、/*roundu*/ false);

    }
}

//根据给定的输出限制调整报价金额和步进金额
template <class TIn, class TOut>
static
void limitStepOut (Quality const& ofrQ,
    TAmounts<TIn, TOut>& ofrAmt,
    TAmounts<TIn, TOut>& stpAmt,
    TOut& ownerGives,
    std::uint32_t transferRateIn,
    std::uint32_t transferRateOut,
    TOut const& limit)
{
    if (limit < stpAmt.out)
    {
        stpAmt.out = limit;
        ownerGives = mulRatio (
            /*amt.out、transferrateout、quality_one、/*roundup*/false）；
        oframt=ofrq.ceil-out（oframt，stpamt.out）；
        stpamt.in=多比率（
            oframt.in，transferratein，quality_one，/*roundu*/ true);

    }
}

template <class TIn, class TOut, class TDerived>
template <class Callback>
std::pair<boost::container::flat_set<uint256>, std::uint32_t>
BookStep<TIn, TOut, TDerived>::forEachOffer (
    PaymentSandbox& sb,
    ApplyView& afView,
    bool prevStepRedeems,
    Callback& callback) const
{
//向发盘人收费，而不是向发盘人收费
//即使所有者与发行人相同，也要收取费用。
//（旧代码不收费）
//计算给接受者的金额和报价所有者收取的金额
    auto rate = [this, &sb](AccountID const& id)->std::uint32_t
    {
        if (isXRP (id) || id == this->strandDst_)
            return QUALITY_ONE;
        return transferRate (sb, id).value;
    };

    std::uint32_t const trIn = prevStepRedeems
        ? rate (book_.in.account)
        : QUALITY_ONE;
//始终收取转让费，即使所有人是发行人
    std::uint32_t const trOut = ownerPaysTransferFee_
        ? rate (book_.out.account)
        : QUALITY_ONE;

    typename FlowOfferStream<TIn, TOut>::StepCounter
        counter (maxOffersToConsume_, j_);

    FlowOfferStream<TIn, TOut> offers (
        sb, afView, book_, sb.parentCloseTime (), counter, j_);

    bool const flowCross = afView.rules().enabled(featureFlowCross);
    bool offerAttempted = false;
    boost::optional<Quality> ofrQ;
    while (offers.step ())
    {
        auto& offer = offers.tip ();

//请注意，offer.quality（）返回（非可选）质量。所以
//OFRQ在循环的这个点以下使用总是安全的。
        if (!ofrQ)
            ofrQ = offer.quality ();
        else if (*ofrQ != offer.quality ())
            break;

        if (static_cast<TDerived const*>(this)->limitSelfCrossQuality (
            strandSrc_, strandDst_, offer, ofrQ, offers, offerAttempted))
                continue;

//确保要约所有人有权拥有发行人的借据。
//帐户可以始终拥有XRP或其自己的IOU。
        if (flowCross &&
            (!isXRP (offer.issueIn().currency)) &&
            (offer.owner() != offer.issueIn().account))
        {
            auto const& issuerID = offer.issueIn().account;
            auto const issuer = afView.read (keylet::account (issuerID));
            if (issuer && ((*issuer)[sfFlags] & lsfRequireAuth))
            {
//颁发者需要授权。看看报价人是否有。
                auto const& ownerID = offer.owner();
                auto const authFlag =
                    issuerID > ownerID ? lsfHighAuth : lsfLowAuth;

                auto const line = afView.read (keylet::line (
                    ownerID, issuerID, offer.issueIn().currency));

                if (!line || (((*line)[sfFlags] & authFlag) == 0))
                {
//要约所有人未被授权持有发行人的IOU。
//即使没有交叉，也要取消此报价。
                    offers.permRmOffer (offer.key());
                    if (!offerAttempted)
//只有在以前没有尝试过的情况下才能更改质量。
                        ofrQ = boost::none;
//此继续操作将导致offers.step（）删除该offere。
                    continue;
                }
            }
        }

        if (! static_cast<TDerived const*>(this)->checkQualityThreshold(offer))
            break;

        auto const ofrInRate =
            static_cast<TDerived const*>(this)->getOfrInRate (
                prevStep_, offer, trIn);

        auto const ofrOutRate =
            static_cast<TDerived const*>(this)->getOfrOutRate (
                prevStep_, offer, strandDst_, trOut);

        auto ofrAmt = offer.amount ();
        auto stpAmt = make_Amounts (
            /*比率（oframt.in，ofrinrate，quality_one，/*roundup*/true）
            OfRAMT；out；

        //业主支付过户费。
        自动所有者给出=
            多重比率（OFRAmt.out，OFROUTRATE，Quality_One，/*Roundu*/ false);

        auto const funds =
            (offer.owner () == offer.issueOut ().account)
? ownerGives //要约所有人是发行人；他们有无限的资金
            : offers.ownerFunds ();

        if (funds < ownerGives)
        {
//我们已经知道了offer.owner（）！=offer.issueout（）。帐户
            ownerGives = funds;
            stpAmt.out = mulRatio (
                /*Ergives，Quality_One，Ofroutrate，/*Roundup*/false）；
            oframt=ofrq->ceil_out（oframt，stpamt.out）；
            stpamt.in=多比率（
                Oframt.in，Ofrinrate，Quality_One，/*圆形*/ true);

        }

        offerAttempted = true;
        if (!callback (
            offer, ofrAmt, stpAmt, ownerGives, ofrInRate, ofrOutRate))
            break;
    }

    return {offers.permToRemove (), counter.count()};
}

template <class TIn, class TOut, class TDerived>
void BookStep<TIn, TOut, TDerived>::consumeOffer (
    PaymentSandbox& sb,
    TOffer<TIn, TOut>& offer,
    TAmounts<TIn, TOut> const& ofrAmt,
    TAmounts<TIn, TOut> const& stepAmt,
    TOut const& ownerGives) const
{
//报价的所有者得到了Oframt。oframt和stepamt的区别
//是转到book\in.帐户的转账费
    {
        auto const dr = accountSend (sb, book_.in.account, offer.owner (),
            toSTAmount (ofrAmt.in, book_.in), j_);
        if (dr != tesSUCCESS)
            Throw<FlowException> (dr);
    }

//要约所有人支付“所有者赠予”。所有者与
//stepamt是一笔转到book_uuu.out.account的转账费用。
    {
        auto const cr = accountSend (sb, offer.owner (), book_.out.account,
            toSTAmount (ownerGives, book_.out), j_);
        if (cr != tesSUCCESS)
            Throw<FlowException> (cr);
    }

    offer.consume (sb, ofrAmt);
}

template<class TCollection>
static
auto sum (TCollection const& col)
{
    using TResult = std::decay_t<decltype (*col.begin ())>;
    if (col.empty ())
        return TResult{beast::zero};
    return std::accumulate (col.begin () + 1, col.end (), *col.begin ());
};

template<class TIn, class TOut, class TDerived>
std::pair<TIn, TOut>
BookStep<TIn, TOut, TDerived>::revImp (
    PaymentSandbox& sb,
    ApplyView& afView,
    boost::container::flat_set<uint256>& ofrsToRm,
    TOut const& out)
{
    cache_.reset ();

    TAmounts<TIn, TOut> result (beast::zero, beast::zero);

    auto remainingOut = out;

    boost::container::flat_multiset<TIn> savedIns;
    savedIns.reserve(64);
    boost::container::flat_multiset<TOut> savedOuts;
    savedOuts.reserve(64);

    /*联邦金额将由业主资金调整（可能与报价不同）
      金额-tho始终<=）
      返回“真”继续接收报价，返回“假”停止接收报价。
    **/

    auto eachOffer =
        [&](TOffer<TIn, TOut>& offer,
            TAmounts<TIn, TOut> const& ofrAmt,
            TAmounts<TIn, TOut> const& stpAmt,
            TOut const& ownerGives,
            std::uint32_t transferRateIn,
            std::uint32_t transferRateOut) mutable -> bool
    {
        if (remainingOut <= beast::zero)
            return false;

        if (stpAmt.out <= remainingOut)
        {
            savedIns.insert(stpAmt.in);
            savedOuts.insert(stpAmt.out);
            result = TAmounts<TIn, TOut>(sum (savedIns), sum(savedOuts));
            remainingOut = out - result.out;
            this->consumeOffer (sb, offer, ofrAmt, stpAmt, ownerGives);
//即使付款满意，也要退回真实的信用证。
//我们需要接受这个提议
            return true;
        }
        else
        {
            auto ofrAdjAmt = ofrAmt;
            auto stpAdjAmt = stpAmt;
            auto ownerGivesAdj = ownerGives;
            limitStepOut (offer.quality (), ofrAdjAmt, stpAdjAmt, ownerGivesAdj,
                transferRateIn, transferRateOut, remainingOut);
            remainingOut = beast::zero;
            savedIns.insert (stpAdjAmt.in);
            savedOuts.insert (remainingOut);
            result.in = sum(savedIns);
            result.out = out;
            this->consumeOffer (sb, offer, ofrAdjAmt, stpAdjAmt, ownerGivesAdj);

//如果两个借据金额的尾数相差不到十，那么
//减去它们会得到零的结果。这可能导致支票
//（stpamt.out>remainingout）错误地认为报价将得到资助
//减去Remainingin后。
            if (fix1298(sb.parentCloseTime()))
                return offer.fully_consumed();
            else
                return false;
        }
    };

    {
        auto const prevStepRedeems = prevStep_ && prevStep_->redeems (sb, false);
        auto const r = forEachOffer (sb, afView, prevStepRedeems, eachOffer);
        boost::container::flat_set<uint256> toRm = std::move(std::get<0>(r));
        std::uint32_t const offersConsumed = std::get<1>(r);
        ofrsToRm.insert (boost::container::ordered_unique_range_t{},
            toRm.begin (), toRm.end ());

        if (offersConsumed >= maxOffersToConsume_)
        {
//迭代太多，将此链标记为非活动
            if (!afView.rules().enabled(fix1515))
            {
//不要使用流动性
                cache_.emplace(beast::zero, beast::zero);
                return {beast::zero, beast::zero};
            }

//使用流动性，但使用它将链标记为不活动，因此
//它不用了
            inactive_ = true;
        }
    }

    switch(remainingOut.signum())
    {
        case -1:
        {
//出了大问题
            JLOG (j_.error ())
                << "BookStep remainingOut < 0 " << to_string (remainingOut);
            assert (0);
            cache_.emplace (beast::zero, beast::zero);
            return {beast::zero, beast::zero};
        }
        case 0:
        {
//由于标准化，remainingout可以为零
//result.out==输出。force result.out==在这种情况下为out
            result.out = out;
        }
    }

    cache_.emplace (result.in, result.out);
    return {result.in, result.out};
}

template<class TIn, class TOut, class TDerived>
std::pair<TIn, TOut>
BookStep<TIn, TOut, TDerived>::fwdImp (
    PaymentSandbox& sb,
    ApplyView& afView,
    boost::container::flat_set<uint256>& ofrsToRm,
    TIn const& in)
{
    assert(cache_);

    TAmounts<TIn, TOut> result (beast::zero, beast::zero);

    auto remainingIn = in;

    boost::container::flat_multiset<TIn> savedIns;
    savedIns.reserve(64);
    boost::container::flat_multiset<TOut> savedOuts;
    savedOuts.reserve(64);

//联邦金额将由业主资金调整（可能与报价不同）
//金额-tho始终<=）
    auto eachOffer =
        [&](TOffer<TIn, TOut>& offer,
            TAmounts<TIn, TOut> const& ofrAmt,
            TAmounts<TIn, TOut> const& stpAmt,
            TOut const& ownerGives,
            std::uint32_t transferRateIn,
            std::uint32_t transferRateOut) mutable -> bool
    {
        assert(cache_);

        if (remainingIn <= beast::zero)
            return false;

        bool processMore = true;
        auto ofrAdjAmt = ofrAmt;
        auto stpAdjAmt = stpAmt;
        auto ownerGivesAdj = ownerGives;

        typename boost::container::flat_multiset<TOut>::const_iterator lastOut;
        if (stpAmt.in <= remainingIn)
        {
            savedIns.insert(stpAmt.in);
            lastOut = savedOuts.insert(stpAmt.out);
            result = TAmounts<TIn, TOut>(sum (savedIns), sum(savedOuts));
//即使stepamt.in==remainingin，也要接受报价
            processMore = true;
        }
        else
        {
            limitStepIn (offer.quality (), ofrAdjAmt, stpAdjAmt, ownerGivesAdj,
                transferRateIn, transferRateOut, remainingIn);
            savedIns.insert (remainingIn);
            lastOut = savedOuts.insert (stpAdjAmt.out);
            result.out = sum (savedOuts);
            result.in = in;

            processMore = false;
        }

        if (result.out > cache_->out && result.in <= cache_->in)
        {
//这一步在向前通过时产生的输出比
//使用相同的输入（或更少）时反向通过。如果我们
//计算生成缓存输出所需的输入
//（在反向步骤中产生）且输入等于
//在前进步骤中消耗的输入，然后消耗
//向前步骤中提供的输入并生成输出
//从反向步骤请求。
            auto const lastOutAmt = *lastOut;
            savedOuts.erase(lastOut);
            auto const remainingOut = cache_->out - sum (savedOuts);
            auto ofrAdjAmtRev = ofrAmt;
            auto stpAdjAmtRev = stpAmt;
            auto ownerGivesAdjRev = ownerGives;
            limitStepOut (offer.quality (), ofrAdjAmtRev, stpAdjAmtRev,
                ownerGivesAdjRev, transferRateIn, transferRateOut,
                remainingOut);

            if (stpAdjAmtRev.in == remainingIn)
            {
                result.in = in;
                result.out = cache_->out;

                savedIns.clear();
                savedIns.insert(result.in);
                savedOuts.clear();
                savedOuts.insert(result.out);

                ofrAdjAmt = ofrAdjAmtRev;
                stpAdjAmt.in = remainingIn;
                stpAdjAmt.out = remainingOut;
                ownerGivesAdj = ownerGivesAdjRev;
            }
            else
            {
//这很可能是个问题，会被抓住的
//以后再检查
                savedOuts.insert (lastOutAmt);
            }
        }

        remainingIn = in - result.in;
        this->consumeOffer (sb, offer, ofrAdjAmt, stpAdjAmt, ownerGivesAdj);

//如果两个借据金额的尾数相差不到十，那么
//减去它们会得到零的结果。这可能导致支票
//（stpamt.in>remainingin）错误地认为报价将得到资助
//减去Remainingin后。
        if (fix1298(sb.parentCloseTime()))
            processMore = processMore || offer.fully_consumed();

        return processMore;
    };

    {
        auto const prevStepRedeems = prevStep_ && prevStep_->redeems (sb, true);
        auto const r = forEachOffer (sb, afView, prevStepRedeems, eachOffer);
        boost::container::flat_set<uint256> toRm = std::move(std::get<0>(r));
        std::uint32_t const offersConsumed = std::get<1>(r);
        ofrsToRm.insert (boost::container::ordered_unique_range_t{},
            toRm.begin (), toRm.end ());

        if (offersConsumed >= maxOffersToConsume_)
        {
//迭代次数太多，将此股标记为不活动（干）
            if (!afView.rules().enabled(fix1515))
            {
//不要使用流动性
                cache_.emplace(beast::zero, beast::zero);
                return {beast::zero, beast::zero};
            }

//使用流动性，但使用它将链标记为不活动，因此
//它不用了
            inactive_ = true;
        }
    }

    switch(remainingIn.signum())
    {
        case -1:
        {
//出了大问题
            JLOG (j_.error ())
                << "BookStep remainingIn < 0 " << to_string (remainingIn);
            assert (0);
            cache_.emplace (beast::zero, beast::zero);
            return {beast::zero, beast::zero};
        }
        case 0:
        {
//由于标准化，remainingin可以为零
//result.in==英寸。对于这种情况，force result.in==in
            result.in = in;
        }
    }

    cache_.emplace (result.in, result.out);
    return {result.in, result.out};
}

template<class TIn, class TOut, class TDerived>
std::pair<bool, EitherAmount>
BookStep<TIn, TOut, TDerived>::validFwd (
    PaymentSandbox& sb,
    ApplyView& afView,
    EitherAmount const& in)
{
    if (!cache_)
    {
        JLOG (j_.trace()) << "Expected valid cache in validFwd";
        return {false, EitherAmount (TOut (beast::zero))};
    }

    auto const savCache = *cache_;

    try
    {
        boost::container::flat_set<uint256> dummy;
fwdImp (sb, afView, dummy, get<TIn> (in));  //更改缓存
    }
    catch (FlowException const&)
    {
        return {false, EitherAmount (TOut (beast::zero))};
    }

    if (!(checkNear (savCache.in, cache_->in) &&
            checkNear (savCache.out, cache_->out)))
    {
        JLOG (j_.error()) <<
            "Strand re-execute check failed." <<
            " ExpectedIn: " << to_string (savCache.in) <<
            " CachedIn: " << to_string (cache_->in) <<
            " ExpectedOut: " << to_string (savCache.out) <<
            " CachedOut: " << to_string (cache_->out);
        return {false, EitherAmount (cache_->out)};
    }
    return {true, EitherAmount (cache_->out)};
}

template<class TIn, class TOut, class TDerived>
TER
BookStep<TIn, TOut, TDerived>::check(StrandContext const& ctx) const
{
    if (book_.in == book_.out)
    {
        JLOG (j_.debug()) << "BookStep: Book with same in and out issuer " << *this;
        return temBAD_PATH;
    }
    if (!isConsistent (book_.in) || !isConsistent (book_.out))
    {
        JLOG (j_.debug()) << "Book: currency is inconsistent with issuer." << *this;
        return temBAD_PATH;
    }

//不允许两本书输出同一期。这可能导致
//一步到位，另一步到位。
    if (!ctx.seenBookOuts.insert (book_.out).second ||
        ctx.seenDirectIssues[0].count (book_.out))
    {
        JLOG (j_.debug()) << "BookStep: loop detected: " << *this;
        return temBAD_PATH_LOOP;
    }

    if (ctx.view.rules().enabled(fix1373) &&
        ctx.seenDirectIssues[1].count(book_.out))
    {
        JLOG(j_.debug()) << "BookStep: loop detected: " << *this;
        return temBAD_PATH_LOOP;
    }

    if (fix1443(ctx.view.info().parentCloseTime))
    {
        if (ctx.prevStep)
        {
            if (auto const prev = ctx.prevStep->directStepSrcAcct())
            {
                auto const& view = ctx.view;
                auto const& cur = book_.in.account;

                auto sle =
                    view.read(keylet::line(*prev, cur, book_.in.currency));
                if (!sle)
                    return terNO_LINE;
                if ((*sle)[sfFlags] &
                    ((cur > *prev) ? lsfHighNoRipple : lsfLowNoRipple))
                    return terNO_RIPPLE;
            }
        }
    }

    return tesSUCCESS;
}

//————————————————————————————————————————————————————————————————————————————————————————————————————————————————

namespace test
{
//测试所需

template <class TIn, class TOut, class TDerived>
static
bool equalHelper (Step const& step, ripple::Book const& book)
{
    if (auto bs = dynamic_cast<BookStep<TIn, TOut, TDerived> const*> (&step))
        return book == bs->book ();
    return false;
}

bool bookStepEqual (Step const& step, ripple::Book const& book)
{
    bool const inXRP = isXRP (book.in.currency);
    bool const outXRP = isXRP (book.out.currency);
    if (inXRP && outXRP)
        return equalHelper<XRPAmount, XRPAmount,
            BookPaymentStep<XRPAmount, XRPAmount>> (step, book);
    if (inXRP && !outXRP)
        return equalHelper<XRPAmount, IOUAmount,
            BookPaymentStep<XRPAmount, IOUAmount>> (step, book);
    if (!inXRP && outXRP)
        return equalHelper<IOUAmount, XRPAmount,
            BookPaymentStep<IOUAmount, XRPAmount>> (step, book);
    if (!inXRP && !outXRP)
        return equalHelper<IOUAmount, IOUAmount,
            BookPaymentStep<IOUAmount, IOUAmount>> (step, book);
    return false;
}
}

//————————————————————————————————————————————————————————————————————————————————————————————————————————————————

template <class TIn, class TOut>
static
std::pair<TER, std::unique_ptr<Step>>
make_BookStepHelper (
    StrandContext const& ctx,
    Issue const& in,
    Issue const& out)
{
    TER ter = tefINTERNAL;
    std::unique_ptr<Step> r;
    if (ctx.offerCrossing)
    {
        auto offerCrossingStep =
            std::make_unique<BookOfferCrossingStep<TIn, TOut>> (ctx, in, out);
        ter = offerCrossingStep->check (ctx);
        r = std::move (offerCrossingStep);
    }
else //付款
    {
        auto paymentStep =
            std::make_unique<BookPaymentStep<TIn, TOut>> (ctx, in, out);
        ter = paymentStep->check (ctx);
        r = std::move (paymentStep);
    }
    if (ter != tesSUCCESS)
        return {ter, nullptr};

    return {tesSUCCESS, std::move(r)};
}

std::pair<TER, std::unique_ptr<Step>>
make_BookStepII (
    StrandContext const& ctx,
    Issue const& in,
    Issue const& out)
{
    return make_BookStepHelper<IOUAmount, IOUAmount> (ctx, in, out);
}

std::pair<TER, std::unique_ptr<Step>>
make_BookStepIX (
    StrandContext const& ctx,
    Issue const& in)
{
    return make_BookStepHelper<IOUAmount, XRPAmount> (ctx, in, xrpIssue());
}

std::pair<TER, std::unique_ptr<Step>>
make_BookStepXI (
    StrandContext const& ctx,
    Issue const& out)
{
    return make_BookStepHelper<XRPAmount, IOUAmount> (ctx, xrpIssue(), out);
}

} //涟漪


// (C) Copyright Tobias Schwinger
//
// Use modification and distribution are subject to the boost Software License,
// Version 1.0. (See http://www.boost.org/LICENSE_1_0.txt).

//------------------------------------------------------------------------------
//
// This example implements a utility to accept a type expression, that may
// contain commas to a macro.
//
//
// Detailed description
// ====================
//
// Accepting a type as macro argument can cause problems if the type expression
// contains commas:
//
//    #define MY_MACRO(a_type)
//    ...
//    MY_MACRO(std::map<int,int>)  // ERROR (wrong number of macro arguments)
//
// This problem can be solved by pasing using a parenthesized type
//
//    MY_MACRO((std::map<int,int>) // OK
//
// but then there is no way to remove the parentheses in the macro argument
// with the preprocessor.
// We can, however, form a pointer to a function with a single argument (the
// parentheses become part of the type) and extract the argument with template
// metaprogramming:
//
//   // Inside the macro definition
//
//   typename mpl::front< parameter_types<void(*)a_type> >::type
//
// This code snippet does not read too expressive so we use another macro
// to encapsulate the solution:
//
//   // Inside the macro definition
//
//   BOOST_EXAMPLE_MACRO_TYPE_ARGUMENT(a_type)
//
// As a generalization of this technique we can accept a comma-separated list of
// types. Omitting the mpl::front invocation gives us an MPL-sequence.
//
//
// Limitations
// ===========
//
// - only works for types that are valid function arguments
//
// Acknowledgments
// ===============
//
// Thanks go to Dave Abrahams for letting me know this technique.

#ifndef BOOST_EXAMPLE_MACRO_TYPE_ARGUMENT_HPP_INCLUDED
#define BOOST_EXAMPLE_MACRO_TYPE_ARGUMENT_HPP_INCLUDED

#include <boost/function_types/parameter_types.hpp>
#include <boost/mpl/front.hpp>

#define BOOST_EXAMPLE_MACRO_TYPE_ARGUMENT(parenthesized_type)                  \
    boost::mpl::front<                                                         \
        BOOST_EXAMPLE_MACRO_TYPE_LIST_ARGUMENT(parenthesized_type) >::type

#define BOOST_EXAMPLE_MACRO_TYPE_LIST_ARGUMENT(parenthesized_types)            \
  ::boost::function_types::parameter_types< void(*) parenthesized_types >

#endif


// Copyright (C) 2011-2012 by the BEM++ Authors
//
// 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 "bempp/common/config_trilinos.hpp"

#include "synthetic_integral_operator.hpp"

#include "abstract_boundary_operator_pseudoinverse.hpp"
#include "boundary_operator.hpp"
#include "context.hpp"
#include "discrete_boundary_operator.hpp"
#include "discrete_sparse_boundary_operator.hpp"
#include "identity_operator.hpp"
#include "sparse_inverse.hpp"
#include "transposed_discrete_boundary_operator.hpp"

#include "../common/boost_make_shared_fwd.hpp"
#include "../common/shared_ptr.hpp"
#include "../fiber/explicit_instantiation.hpp"

#ifdef WITH_TRILINOS

#include <boost/make_shared.hpp>
#endif

#include <Epetra_CrsGraph.h>
#include <Epetra_CrsMatrix.h>
#include <EpetraExt_MatrixMatrix.h>

#include <tbb/tick_count.h>

namespace Bempp {

namespace {

template <typename T> const T &vectorFirstElement(const std::vector<T> &v) {
  if (v.empty())
    throw std::invalid_argument(
        "SyntheticIntegralOperator::SyntheticIntegralOperator(): "
        "lists of test and trial-side operators must not be empty");
  return v.front();
}

template <typename ResultType>
std::vector<shared_ptr<const DiscreteBoundaryOperator<ResultType>>>
coalesceTestOperators(
    const std::vector<shared_ptr<const DiscreteBoundaryOperator<ResultType>>> &
        discreteLocalOps,
    const shared_ptr<const Epetra_CrsMatrix> &idInverse) {
  typedef DiscreteBoundaryOperator<ResultType> DiscreteOp;
  typedef DiscreteSparseBoundaryOperator<ResultType> SparseOp;
  std::vector<shared_ptr<const DiscreteOp>> result(discreteLocalOps.size());

  for (size_t i = 0; i < discreteLocalOps.size(); ++i) {
    if (!discreteLocalOps[i])
      throw std::runtime_error(
          "coalesceTrialOperators(): null pointer to operator detected");
    shared_ptr<const SparseOp> op =
        boost::dynamic_pointer_cast<const SparseOp>(discreteLocalOps[i]);
    if (op) {
      shared_ptr<const Epetra_CrsMatrix> opMat = op->epetraMatrix();
      shared_ptr<Epetra_CrsMatrix> composition(
          new Epetra_CrsMatrix(Copy, opMat->Graph()));
      int err = EpetraExt::MatrixMatrix::Multiply(
          *opMat, false /* transpose */, *idInverse, false, *composition);
      if (err != 0)
        throw std::runtime_error(
            "SyntheticIntegralOperator::assembleWeakFormImpl(): "
            "Epetra matrix-matrix multiplication (1) failed");
      result[i].reset(new SparseOp(composition));
    } else
      throw std::runtime_error(
          "SyntheticIntegralOperator::coalesceTestOperators(): "
          "local operator not represented by a sparse matrix");
  }
  return result;
}

template <typename ResultType>
std::vector<shared_ptr<const DiscreteBoundaryOperator<ResultType>>>
coalesceTrialOperators(
    const std::vector<shared_ptr<const DiscreteBoundaryOperator<ResultType>>> &
        discreteLocalOps,
    const shared_ptr<const Epetra_CrsMatrix> &idInverse) {
  typedef DiscreteBoundaryOperator<ResultType> DiscreteOp;
  typedef DiscreteSparseBoundaryOperator<ResultType> SparseOp;
  std::vector<shared_ptr<const DiscreteOp>> result(discreteLocalOps.size());

  for (size_t i = 0; i < discreteLocalOps.size(); ++i) {
    if (!discreteLocalOps[i])
      throw std::runtime_error(
          "coalesceTrialOperators(): null pointer to operator detected");
    shared_ptr<const SparseOp> op =
        boost::dynamic_pointer_cast<const SparseOp>(discreteLocalOps[i]);
    if (op) {
      shared_ptr<const Epetra_CrsMatrix> opMat = op->epetraMatrix();
      shared_ptr<Epetra_CrsMatrix> composition(
          new Epetra_CrsMatrix(Copy, opMat->Graph()));
      int err = EpetraExt::MatrixMatrix::Multiply(
          *idInverse, true /* transpose */, *opMat, false, *composition);
      if (err != 0)
        throw std::runtime_error(
            "SyntheticIntegralOperator::assembleWeakFormImpl(): "
            "Epetra matrix-matrix multiplication (1) failed");
      result[i].reset(new SparseOp(composition));
    } else
      throw std::runtime_error(
          "SyntheticIntegralOperator::coalesceTrialOperators(): "
          "local operator not represented by a sparse matrix");
  }
  return result;
}

template <typename ResultType>
std::vector<shared_ptr<const DiscreteBoundaryOperator<ResultType>>>
transposeTestOperators(
    const std::vector<shared_ptr<const DiscreteBoundaryOperator<ResultType>>> &
        discreteLocalOps,
    bool hermitian) {
  typedef DiscreteBoundaryOperator<ResultType> DiscreteOp;
  typedef DiscreteSparseBoundaryOperator<ResultType> SparseOp;
  std::vector<shared_ptr<const DiscreteOp>> result(discreteLocalOps.size());

  for (size_t i = 0; i < discreteLocalOps.size(); ++i) {
    if (!discreteLocalOps[i])
      throw std::runtime_error(
          "coalesceTrialOperators(): null pointer to operator detected");
    shared_ptr<const SparseOp> op =
        boost::dynamic_pointer_cast<const SparseOp>(discreteLocalOps[i]);
    if (op) {
      int mode = op->transpositionMode();
      if (mode & TRANSPOSE)
        mode &= ~TRANSPOSE;
      else
        mode |= TRANSPOSE;
      result[i].reset(new SparseOp(op->epetraMatrix(), op->symmetryMode(),
                                   static_cast<TranspositionMode>(mode)));
    } else
      result[i].reset(new TransposedDiscreteBoundaryOperator<ResultType>(
          hermitian ? CONJUGATE_TRANSPOSE : TRANSPOSE, discreteLocalOps[i]));
  }
  return result;
}

template <typename BasisFunctionType, typename ResultType>
shared_ptr<const Space<BasisFunctionType>> determineDomain(
    const std::vector<BoundaryOperator<BasisFunctionType, ResultType>> &
        testLocalOps,
    const BoundaryOperator<BasisFunctionType, ResultType> &integralOp,
    const std::vector<BoundaryOperator<BasisFunctionType, ResultType>> &
        trialLocalOps,
    int symmetry) {
  if (trialLocalOps.empty())
    if (testLocalOps.empty())
      throw std::invalid_argument(
          "SyntheticIntegralOperator::SyntheticIntegralOperator(): "
          "testLocalOps and trialLocalOps must not both be empty.");
    else {
      if (symmetry & (SYMMETRIC | HERMITIAN))
        return testLocalOps[0].dualToRange();
      else
        return integralOp.domain();
    }
  else
    return trialLocalOps[0].domain();
}

template <typename BasisFunctionType, typename ResultType>
shared_ptr<const Space<BasisFunctionType>> determineRange(
    const std::vector<BoundaryOperator<BasisFunctionType, ResultType>> &
        testLocalOps,
    const BoundaryOperator<BasisFunctionType, ResultType> &integralOp) {
  if (testLocalOps.empty())
    return integralOp.range();
  else
    return testLocalOps[0].range();
}

template <typename BasisFunctionType, typename ResultType>
shared_ptr<const Space<BasisFunctionType>> determineDualToRange(
    const std::vector<BoundaryOperator<BasisFunctionType, ResultType>> &
        testLocalOps,
    const BoundaryOperator<BasisFunctionType, ResultType> &integralOp) {
  if (testLocalOps.empty())
    return integralOp.dualToRange();
  else
    return testLocalOps[0].dualToRange();
}

} // namespace

template <typename BasisFunctionType, typename ResultType>
SyntheticIntegralOperator<BasisFunctionType, ResultType>::
    SyntheticIntegralOperator(
        const std::vector<BoundaryOperator<BasisFunctionType, ResultType>> &
            testLocalOps,
        const BoundaryOperator<BasisFunctionType, ResultType> &integralOp,
        const std::vector<BoundaryOperator<BasisFunctionType, ResultType>> &
            trialLocalOps,
        const std::string &label, int syntheseSymmetry)
    : Base(determineDomain(testLocalOps, integralOp, trialLocalOps,
                           syntheseSymmetry),
           determineRange(testLocalOps, integralOp),
           determineDualToRange(testLocalOps, integralOp), label,
           syntheseSymmetry & integralOp.abstractOperator()->symmetry()),
      m_integralOp(integralOp), m_testLocalOps(testLocalOps),
      m_trialLocalOps(trialLocalOps), m_syntheseSymmetry(syntheseSymmetry) {
  // Note: the code does not at present distinguish properly between symmetric
  // and/or hermitian operators (in fact symmetry handling is, frankly, a
  // mess). We get away with this because sparse operators can only contain
  // real entries anyway. To be fixed in future.

  checkIntegralOperator();
  if (m_testLocalOps.size() > 0 && m_trialLocalOps.size() > 0 &&
      m_testLocalOps.size() != m_trialLocalOps.size())
    throw std::invalid_argument(
        "SyntheticIntegralOperator::SyntheticIntegralOperator(): "
        "if both testLocalOps and trialLocalOps are non-empty, "
        "both must have the same number of elements");
  checkTestLocalOperators();
  if ((syntheseSymmetry & SYMMETRIC) || (syntheseSymmetry & HERMITIAN)) {
    if (m_testLocalOps.empty())
      throw std::invalid_argument(
          "SyntheticIntegralOperator::SyntheticIntegralOperator(): "
          "symmetric operator requested but testLocalOps empty");
    m_trialLocalOps.clear();
  } else
    checkTrialLocalOperators();
  if (m_testLocalOps.empty() && m_trialLocalOps.empty())
    throw std::invalid_argument(
        "SyntheticIntegralOperator::SyntheticIntegralOperator(): "
        "testLocalOps and trialLocalOps must not both be empty.");
}

template <typename BasisFunctionType, typename ResultType>
bool SyntheticIntegralOperator<BasisFunctionType, ResultType>::isLocal() const {
  return false;
}

template <typename BasisFunctionType, typename ResultType>
void SyntheticIntegralOperator<BasisFunctionType,
                               ResultType>::checkIntegralOperator() const {
  if (!m_integralOp.isInitialized())
    throw std::invalid_argument(
        "SyntheticIntegralOperator::checkIntegralOperator(): "
        "all operators must be initialized");
}

template <typename BasisFunctionType, typename ResultType>
void SyntheticIntegralOperator<BasisFunctionType,
                               ResultType>::checkTestLocalOperators() const {
  size_t localOperatorCount = m_testLocalOps.size();
  for (size_t i = 0; i < localOperatorCount; ++i)
    if (!m_testLocalOps[i].isInitialized())
      throw std::invalid_argument(
          "SyntheticIntegralOperator::checkTestLocalOperators(): "
          "all operators must be initialized");
  for (size_t i = 0; i < localOperatorCount; ++i)
    if (!m_testLocalOps[i].abstractOperator()->isLocal())
      throw std::invalid_argument(
          "SyntheticIntegralOperator::checkTestLocalOperators(): "
          "all test operators must be local");
  for (size_t i = 0; i < localOperatorCount; ++i)
    if (m_testLocalOps[i].domain() != m_integralOp.dualToRange())
      throw std::invalid_argument(
          "SyntheticIntegralOperator::checkTestLocalOperators(): "
          "domain of all test operators must be identical with the dual "
          "to range of the integral operator");
  for (size_t i = 1; i < localOperatorCount; ++i)
    if (m_testLocalOps[i].domain() != m_testLocalOps[0].domain() ||
        m_testLocalOps[i].range() != m_testLocalOps[0].range() ||
        m_testLocalOps[i].dualToRange() != m_testLocalOps[0].dualToRange())
      throw std::invalid_argument(
          "SyntheticIntegralOperator::checkTestLocalOperators(): "
          "test operators have inconsistent spaces");
}

template <typename BasisFunctionType, typename ResultType>
void SyntheticIntegralOperator<BasisFunctionType,
                               ResultType>::checkTrialLocalOperators() const {
  size_t localOperatorCount = m_trialLocalOps.size();
  for (size_t i = 0; i < localOperatorCount; ++i)
    if (!m_trialLocalOps[i].isInitialized())
      throw std::invalid_argument(
          "SyntheticIntegralOperator::checkTrialLocalOperators(): "
          "all operators must be initialized");
  for (size_t i = 0; i < localOperatorCount; ++i)
    if (!m_trialLocalOps[i].abstractOperator()->isLocal())
      throw std::invalid_argument(
          "SyntheticIntegralOperator::checkTrialLocalOperators(): "
          "all test operators must be local");
  for (size_t i = 0; i < localOperatorCount; ++i)
    if (m_trialLocalOps[i].dualToRange() != m_integralOp.domain())
      throw std::invalid_argument(
          "SyntheticIntegralOperator::checkTrialLocalOperators(): "
          "dual to range of all trial operators must be identical with the "
          "domain of the integral operator");
  for (size_t i = 1; i < localOperatorCount; ++i)
    if (m_trialLocalOps[i].domain() != m_trialLocalOps[0].domain() ||
        m_trialLocalOps[i].range() != m_trialLocalOps[0].range() ||
        m_trialLocalOps[i].dualToRange() != m_trialLocalOps[0].dualToRange())
      throw std::invalid_argument(
          "SyntheticIntegralOperator::checkTrialLocalOperators(): "
          "trial operators have inconsistent spaces");
}

template <typename BasisFunctionType, typename ResultType>
shared_ptr<DiscreteBoundaryOperator<ResultType>>
SyntheticIntegralOperator<BasisFunctionType, ResultType>::assembleWeakFormImpl(
    const Context<BasisFunctionType, ResultType> &context) const {
  bool verbose =
      (context.assemblyOptions().verbosityLevel() >= VerbosityLevel::DEFAULT);
  if (verbose)
    std::cout << "Assembling the weak form of operator '" << this->label()
              << "'..." << std::endl;

  tbb::tick_count start = tbb::tick_count::now();

  typedef BoundaryOperator<BasisFunctionType, ResultType> BoundaryOp;
  typedef DiscreteBoundaryOperator<ResultType> DiscreteLinOp;

  bool symmetricMode =
      m_syntheseSymmetry & SYMMETRIC || m_syntheseSymmetry & HERMITIAN;
  // std::cout << "symmetricMode: " << symmetricMode << std::endl;

  // We don't need a persistent shared pointer
  shared_ptr<const Context<BasisFunctionType, ResultType>> internalContext,
      auxContext;
  getContextsForInternalAndAuxiliaryOperators(make_shared_from_ref(context),
                                              internalContext, auxContext);

  shared_ptr<const DiscreteLinOp> discreteIntegralOp = m_integralOp.weakForm();
  std::vector<shared_ptr<const DiscreteLinOp>> discreteTestLocalOps(
      m_testLocalOps.size()),
      discreteTrialLocalOps(m_trialLocalOps.size());
  for (size_t i = 0; i < m_testLocalOps.size(); ++i)
    discreteTestLocalOps[i] = m_testLocalOps[i].weakForm();
  // doesn't do any harm if the list of trial operators is empty
  for (size_t i = 0; i < m_trialLocalOps.size(); ++i)
    discreteTrialLocalOps[i] = m_trialLocalOps[i].weakForm();

  // Calculate the inverse mass matrices
  typedef DiscreteSparseBoundaryOperator<ResultType> SparseOp;
  shared_ptr<const SparseOp> discreteTestId, discreteTrialId;
  shared_ptr<Epetra_CrsMatrix> testInverse, trialInverse;
  if (!discreteTestLocalOps.empty()) {
    BoundaryOp testId = identityOperator(
        // We don't need a persistent shared_ptr since identityOperator
        // will go out of scope at the end of this function anyway.
        // All we need is a weak form.
        auxContext, m_integralOp.dualToRange(), m_integralOp.dualToRange(),
        m_integralOp.dualToRange(), "(" + this->label() + ")_test_id");
    discreteTestId = dynamic_pointer_cast<const SparseOp>(testId.weakForm());
    if (!discreteTestId)
      throw std::runtime_error(
          "SyntheticIntegralOperator::SyntheticIntegralOperator(): "
          "identity operator must be represented by a sparse matrix");

    // NOTE: here we form the explicit inverse of the mass matrix. Of
    // course it is not the right way to do it and we'd be better off using
    // Cholesky decomposition and triangular solves. However, implementing
    // this for sparse matrices would be relatively hard. Moreover, in
    // practice the mass matrix will be block diagonal (since the internal
    // domain and dual to range spaces will be discontinuous), so we only
    // form explicit inverses of very small (e.g. 3 x 3) blocks, and mass
    // matrices are usually well conditioned. So explicit inversion
    // shouldn't cause any significant loss of accuracy.
    testInverse = sparseInverse(*discreteTestId->epetraMatrix());
  }

  if (!discreteTrialLocalOps.empty()) {
    if (m_integralOp.domain() == m_integralOp.dualToRange() && discreteTestId) {
      discreteTrialId = discreteTestId;
      //            discreteTrialInvId = discreteTestInvId;
      trialInverse = testInverse;
    } else {
      BoundaryOp trialId = identityOperator(
          auxContext, m_integralOp.domain(), m_integralOp.domain(),
          m_integralOp.domain(), "(" + this->label() + ")_trial_id");
      discreteTrialId =
          dynamic_pointer_cast<const SparseOp>(trialId.weakForm());
      if (!discreteTrialId)
        throw std::runtime_error(
            "SyntheticIntegralOperator::SyntheticIntegralOperator(): "
            "identity operator must be represented by a sparse matrix");
      trialInverse = sparseInverse(*discreteTrialId->epetraMatrix());
    }
  }

  // Coalesce sparse operators
  discreteTestLocalOps =
      coalesceTestOperators(discreteTestLocalOps, testInverse);
  if (discreteTrialLocalOps.empty()) {
    if (symmetricMode)
      discreteTrialLocalOps = transposeTestOperators(
          discreteTestLocalOps, m_syntheseSymmetry & HERMITIAN);
  } else
    discreteTrialLocalOps =
        coalesceTrialOperators(discreteTrialLocalOps, trialInverse);

  // Now join all the pieces together
  shared_ptr<DiscreteLinOp> result;
  if (discreteTestLocalOps.empty()) {
    result = mul<ResultType>(discreteIntegralOp, discreteTrialLocalOps[0]);
    for (size_t i = 1; i < discreteTestLocalOps.size(); ++i)
      result =
          sum<ResultType>(result, mul<ResultType>(discreteIntegralOp,
                                                  discreteTrialLocalOps[i]));
  } else if (discreteTrialLocalOps.empty()) {
    result = mul<ResultType>(discreteTestLocalOps[0], discreteIntegralOp);
    for (size_t i = 1; i < discreteTestLocalOps.size(); ++i)
      result = sum<ResultType>(
          result, mul<ResultType>(discreteTestLocalOps[i], discreteIntegralOp));
  } else {
    result = mul<ResultType>(
        mul<ResultType>(discreteTestLocalOps[0], discreteIntegralOp),
        discreteTrialLocalOps[0]);
    for (size_t i = 1; i < discreteTestLocalOps.size(); ++i)
      result = sum<ResultType>(
          result, mul<ResultType>(mul<ResultType>(discreteTestLocalOps[i],
                                                  discreteIntegralOp),
                                  discreteTrialLocalOps[i]));
  }

  tbb::tick_count end = tbb::tick_count::now();

  if (verbose)
    std::cout << "Assembly of the weak form of operator '" << this->label()
              << "' took " << (end - start).seconds() << " s" << std::endl;
  return result;
}

template <typename BasisFunctionType, typename ResultType>
void SyntheticIntegralOperator<BasisFunctionType, ResultType>::
    getContextsForInternalAndAuxiliaryOperators(
        const shared_ptr<const Context<BasisFunctionType, ResultType>> &context,
        shared_ptr<const Context<BasisFunctionType, ResultType>> &
            internalContext,
        shared_ptr<const Context<BasisFunctionType, ResultType>> &auxContext) {
  typedef Context<BasisFunctionType, ResultType> Ctx;
  AssemblyOptions assemblyOptions = context->assemblyOptions();
  AcaOptions acaOptions = assemblyOptions.acaOptions();
  acaOptions.mode = AcaOptions::GLOBAL_ASSEMBLY;
  if (assemblyOptions.assemblyMode() == AssemblyOptions::ACA)
    assemblyOptions.switchToAcaMode(acaOptions);
  internalContext.reset(new Ctx(context->quadStrategy(), assemblyOptions));
  auxContext = internalContext;
  if (assemblyOptions.verbosityLevel() < VerbosityLevel::HIGH) {
    // Suppress timing messages from auxiliary operators
    assemblyOptions.setVerbosityLevel(VerbosityLevel::LOW);
    auxContext.reset(new Ctx(context->quadStrategy(), assemblyOptions));
  }
}

FIBER_INSTANTIATE_CLASS_TEMPLATED_ON_BASIS_AND_RESULT(
    SyntheticIntegralOperator);

} // namespace Bempp


/**
 * Copyright Soramitsu Co., Ltd. All Rights Reserved.
 * SPDX-License-Identifier: Apache-2.0
 */

#ifndef KAGOME_STROBE_HPP
#define KAGOME_STROBE_HPP

#include <array>
#include <cstdint>
#include <cstring>
#include <gsl/span>
#include <tuple>
#include <type_traits>

#include <boost/assert.hpp>

#include "crypto/keccak/keccak.h"
#include "primitives/math.hpp"

namespace kagome::primitives {

  /**
   * C++ implementation(Alexander 'iceseer' Lednev) of
   * https://strobe.sourceforge.io/
   */
  class Strobe final {
    static constexpr size_t kBufferSize = 200ull;
    static constexpr size_t kAlignment = 8ull;
    static constexpr uint8_t kStrobeR = 166;

    using Flags = uint8_t;
    using Position = uint8_t;

    static constexpr Flags kFlag_NU = 0x00;  // NU = No Use
    static constexpr Flags kFlag_I = 0x01;
    static constexpr Flags kFlag_A = 0x02;
    static constexpr Flags kFlag_C = 0x04;
    static constexpr Flags kFlag_T = 0x08;
    static constexpr Flags kFlag_M = 0x10;
    static constexpr Flags kFlag_K = 0x20;

    uint8_t raw_data[kBufferSize + kAlignment - 1ull + 3ull];
    uint8_t *const buffer_;
    Position &current_position_;
    Position &begin_position_;
    Flags &current_state_;

    template <typename T, size_t kOffset = 0ull>
    constexpr T *as() {
      static_assert(kOffset < count<T>(), "Overflow!");
      return (reinterpret_cast<T *>(buffer_) + kOffset);  // NOLINT
    }

    template <typename T, size_t kOffset = 0ull>
    constexpr const T *as() const {
      static_assert(kOffset < count<T>(), "Overflow!");
      return (reinterpret_cast<const T *>(buffer_) + kOffset);  // NOLINT
    }

    template <typename T>
    T *as(Position offset) {
      BOOST_ASSERT(static_cast<size_t>(offset) < count<T>());
      return (as<T>() + offset);
    }

    template <typename T>
    static constexpr size_t count() {
      static_assert(!std::is_reference_v<T>, "Can not be a reference type.");
      static_assert(!std::is_pointer_v<T>, "Can not be a pointer.");
      static_assert(std::is_integral_v<T>, "Cast to non integral type.");
      return kBufferSize / sizeof(T);
    }

    template <size_t kOffset, typename T, size_t N>
    void load(const T (&data)[N]) {  // NOLINT
      static_assert(kOffset + N <= count<T>(), "Load overflows!");
      std::memcpy(as<T, kOffset>(), data, N);
    }

    template <typename T, size_t N>
    void absorb(const T (&src)[N]) {
      for (const auto i : src) {
        *as<uint8_t>(current_position_++) ^= static_cast<uint8_t>(i);
        if (kStrobeR == current_position_) {
          runF();
        }
      }
    }

    template <typename T, size_t N>
    void overwrite(const T (&src)[N]) {
      for (const auto i : src) {
        *as<uint8_t>(current_position_++) = static_cast<uint8_t>(i);
        if (kStrobeR == current_position_) {
          runF();
        }
      }
    }

    template <typename T, size_t N>
    void squeeze(T (&src)[N]) {
      for (auto &i : src) {
        i = static_cast<T>(*as<uint8_t>(current_position_));
        *as<uint8_t>(current_position_++) = 0;
        if (kStrobeR == current_position_) {
          runF();
        }
      }
    }

    template <bool kMore, Flags kFlags>
    void beginOp() {
      static_assert((kFlags & kFlag_T) == 0, "T flag doesn't support");
      if constexpr (kMore) {
        BOOST_ASSERT(current_state_ == kFlags);
        return;
      }

      const auto old_begin = begin_position_;
      begin_position_ = current_position_ + 1;
      current_state_ = kFlags;
      absorb({old_begin, kFlags});

      if constexpr (0 != (kFlags & (kFlag_C | kFlag_K))) {
        if (current_position_ != 0) {
          runF();
        }
      }
    }

    void runF() {
      *as<uint8_t>(current_position_) ^= begin_position_;
      *as<uint8_t>(current_position_ + 1) ^= 0x04;
      *as<uint8_t>(kStrobeR + 1) ^= 0x80;
      keccakf(as<uint64_t>());

      current_position_ = 0;
      begin_position_ = 0;
    }

   public:
    Strobe()
        : buffer_{reinterpret_cast<uint8_t *>(
            math::roundUp<kAlignment>(reinterpret_cast<uintptr_t>(raw_data)))},
          current_position_{*(buffer_ + kBufferSize)},
          begin_position_{*(buffer_ + kBufferSize + 1ull)},
          current_state_{*(buffer_ + kBufferSize + 2ull)} {}

    Strobe(const Strobe &other) : Strobe() {
      std::copy(std::begin(other.raw_data),
                std::end(other.raw_data),
                std::begin(raw_data));
    };

    Strobe &operator=(const Strobe &other) {
      std::copy(std::begin(other.raw_data),
                std::end(other.raw_data),
                std::begin(raw_data));
      return *this;
    }

    Strobe(Strobe &&) = delete;
    Strobe &operator=(Strobe &&) = delete;

    template <typename T, size_t N>
    void initialize(const T (&label)[N]) {
      constexpr bool kUseRuntimeCalculation = false;

      if constexpr (kUseRuntimeCalculation) {
        std::memset(as<uint8_t>(), 0, count<uint8_t>());
        load<0ull>((uint8_t[6]){1, kStrobeR + 2, 1, 0, 1, 96});
        load<6ull>("STROBEv1.0.2");
        keccakf(as<uint64_t>());
      } else {
        load<0ull>((uint8_t[kBufferSize]){
            0x9c, 0x6d, 0x16, 0x8f, 0xf8, 0xfd, 0x55, 0xda, 0x2a, 0xa7, 0x3c,
            0x23, 0x55, 0x65, 0x35, 0x63, 0xdc, 0xc,  0x47, 0x5c, 0x55, 0x15,
            0x26, 0xf6, 0x73, 0x3b, 0xea, 0x22, 0xf1, 0x6c, 0xb5, 0x7c, 0xd3,
            0x1f, 0x68, 0x2e, 0x66, 0xe,  0xe9, 0x12, 0x82, 0x4a, 0x77, 0x22,
            0x1,  0xee, 0x13, 0x94, 0x22, 0x6f, 0x4a, 0xfc, 0xb6, 0x2d, 0x33,
            0x12, 0x93, 0xcc, 0x92, 0xe8, 0xa6, 0x24, 0xac, 0xf6, 0xe1, 0xb6,
            0x0,  0x95, 0xe3, 0x22, 0xbb, 0xfb, 0xc8, 0x45, 0xe5, 0xb2, 0x69,
            0x95, 0xfe, 0x7d, 0x7c, 0x84, 0x13, 0x74, 0xd1, 0xff, 0x58, 0x98,
            0xc9, 0x2e, 0xe0, 0x63, 0x6b, 0x6,  0x72, 0x73, 0x21, 0xc9, 0x2a,
            0x60, 0x39, 0x7,  0x3,  0x53, 0x49, 0xcc, 0xbb, 0x1b, 0x92, 0xb7,
            0xb0, 0x5,  0x7e, 0x8f, 0xa8, 0x7f, 0xce, 0xbc, 0x7e, 0x88, 0x65,
            0x6f, 0xcb, 0x45, 0xae, 0x4,  0xbc, 0x34, 0xca, 0xbe, 0xae, 0xbe,
            0x79, 0xd9, 0x17, 0x50, 0xc0, 0xe8, 0xbf, 0x13, 0xb9, 0x66, 0x50,
            0x4d, 0x13, 0x43, 0x59, 0x72, 0x65, 0xdd, 0x88, 0x65, 0xad, 0xf9,
            0x14, 0x9,  0xcc, 0x9b, 0x20, 0xd5, 0xf4, 0x74, 0x44, 0x4,  0x1f,
            0x97, 0xb6, 0x99, 0xdd, 0xfb, 0xde, 0xe9, 0x1e, 0xa8, 0x7b, 0xd0,
            0x9b, 0xf8, 0xb0, 0x2d, 0xa7, 0x5a, 0x96, 0xe9, 0x47, 0xf0, 0x7f,
            0x5b, 0x65, 0xbb, 0x4e, 0x6e, 0xfe, 0xfa, 0xa1, 0x6a, 0xbf, 0xd9,
            0xfb, 0xf6});
      }

      current_position_ = 0;
      current_state_ = kFlag_NU;
      begin_position_ = 0;

      metaAd<false>(label);
    }

    template <bool kMore, typename T, size_t N>
    void ad(const T (&src)[N]) {
      beginOp<kMore, kFlag_A>();
      absorb(src);
    }

    template <bool kMore, typename T, size_t N>
    void metaAd(const T (&label)[N]) {
      beginOp<kMore, kFlag_M | kFlag_A>();
      absorb(label);
    }

    template <bool kMore, typename T, size_t N>
    void prf(T (&data)[N]) {
      beginOp<kMore, kFlag_I | kFlag_A | kFlag_C>();
      squeeze(data);
    }

    template <bool kMore, typename T, size_t N>
    void key(const T (&data)[N]) {
      beginOp<kMore, kFlag_A | kFlag_C>();
      overwrite(data);
    }

    auto data() {
      return gsl::make_span(as<const uint8_t>(), count<uint8_t>() + 3ull);
    }

    auto data() const {
      return gsl::make_span(as<const uint8_t>(), count<uint8_t>() + 3ull);
    }
  };

}  // namespace kagome::primitives

#endif  // KAGOME_STROBE_HPP


/*
 * MIT License
 *
 * Copyright (c) 2015 Alexis LE GOADEC
 *
 * 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 <boost/program_options/cmdline.hpp>
#include <boost/program_options/config.hpp>
#include <boost/program_options/environment_iterator.hpp>
#include <boost/program_options/eof_iterator.hpp>
#include <boost/program_options/errors.hpp>
#include <boost/program_options/option.hpp>
#include <boost/program_options/options_description.hpp>
#include <boost/program_options/parsers.hpp>
#include <boost/program_options/positional_options.hpp>
#include <boost/program_options/value_semantic.hpp>
#include <boost/program_options/variables_map.hpp>

#include <boost/shared_ptr.hpp>
#include <boost/version.hpp>
#include <fstream>
#include <iostream>

#include "include/Client.hh"
#include "include/RandomHypergraphe.hh"

#include "../include/Hypergraph/model/LibType.hh"
#include "../include/Hypergraph/model/Hypergraphe.hh"
#include "../include/Hypergraph/model/HyperFactory.hh"
#include "../include/Hypergraph/model/HyperVertex.hh"
#include "../include/Hypergraph/model/HyperEdge.hh"
#include "../include/Hypergraph/model/MotorAlgorithm.hh"

#include "../include/Hypergraph/algorithm/Dual.hh"
#include "../include/Hypergraph/algorithm/Path.hh"
#include "../include/Hypergraph/algorithm/Helly.hh"
#include "../include/Hypergraph/algorithm/kRegular.hh"
#include "../include/Hypergraph/algorithm/kUniform.hh"
#include "../include/Hypergraph/algorithm/Simple.hh"
#include "../include/Hypergraph/algorithm/Linear.hh"
#include "../include/Hypergraph/algorithm/Connected.hh"
#include "../include/Hypergraph/algorithm/HyperGraphStat.hh"
#include "../include/Hypergraph/algorithm/Isomorph.hh"

#include "../include/Hypergraph/io/WriterFile.hh"
#include "../include/Hypergraph/io/ReaderFile.hh"

int main(int argc, char *argv[]) {

	boost::program_options::options_description desc("Paramètres");
	desc.add_options()
					("version", "Afficher la version")
					("help", "Afficher l'aide")
					("inputfile", boost::program_options::value<std::string>(), "Fichier d'entrée")
					("random", boost::program_options::value<int>(), "Hypergraphe aléatoire de n vertex")
					("adjacence", "Affiche la matrice d'adjacence de l'hypergraphe")
					("dual", "Produit le Dual de l'hypergraphe")
					("kuniform", boost::program_options::value<int>(), "Décide si l'hypergraphe est k-uniforme")
					("linear", "Décide si l'hypergraphe est linéaire")
					("kregular", "Décide si l'hypergraphe est k-regulier")
					("simple", "Décide si l'hypergraphe est simple")
					("helly", "Décide si un hypergraphe possède la propriété de Helly")
					("connexe", "Décide si l'hypergraphe est connexe")
					("isomorph", boost::program_options::value<std::string>(), "Décide si deux hypergraphes sont isomorphe")
					("stat", "Retourne les statistiques de l'hypergraphe")
					("path", "Retourne le chemins")
					("source", boost::program_options::value<int>(), "Source de la reherche de chemins")
					("destination", boost::program_options::value<int>(), "Destination de la recherche de chemins");

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

	if (vm.count("help") || vm.empty()) {
	    std::cout << desc << "\n";
	    return !vm.empty();
	}

	if( vm.count("version") ) {
		std::cout << "HypergraphLib "
				  << VERSION_MAJOR << "."
				  << VERSION_MINOR << "-"
				  << VERSION_BUILD
				  << std::endl
				  << "Université de Caen Basse-Normandie, 2015 - Alexis LE GOADEC."
				  << std::endl
				  << "Boost version: " << BOOST_VERSION << std::endl
				  << "GCC version: " << __VERSION__ << std::endl
#ifdef __x86_64
				  << "x86_64: " << __x86_64 << std::endl
#endif
				  << "---" << std::endl;
		return 0;
	}

	boost::shared_ptr<HypergrapheAbstrait> ptrHpg;

	if( vm.count("inputfile") && vm.count("random")==0 ) {
		std::ifstream ifs(vm["inputfile"].as<std::string>(), std::ifstream::in);

		ReaderFile fReader;
		fReader.readHypergraphe( ifs );
		ifs.close();

		ptrHpg = fReader.getHypergraphe();

	} else if( vm.count("inputfile")==0 && vm.count("random")==0) {
		ReaderFile fReader;
		fReader.readHypergraphe( std::cin );
		ptrHpg = fReader.getHypergraphe();
	}

	// Isomorphism special parameters configuration
	if( vm.count("isomorph") && vm.count("inputfile") ) {

		boost::shared_ptr<HypergrapheAbstrait> ptrHpg2;

		std::ifstream ifs(vm["isomorph"].as<std::string>(), std::ifstream::in);

		ReaderFile fReader;
		fReader.readHypergraphe( ifs );
		ifs.close();

		ptrHpg2 = fReader.getHypergraphe();

		NewAlgorithm2(isomorphHpg, Isomorph, ptrHpg, ptrHpg2);

		MotorAlgorithm::setAlgorithme( isomorphHpg );
		MotorAlgorithm::runAlgorithme();

		RStructure r( isomorphHpg->getResult() );

		if( r.getBooleanResult() ) {
			std::cout << "L'hypergraphe est isomorphe." << std::endl;
		} else {
			std::cout << "L'hypergraphe n'est pas isomorphe." << std::endl;
		}

		return 0;
	}

	if( vm.count("random") ) {
		RandomHypergraphe rHyp;
		rHyp.generateHypergraphe(vm["random"].as<int>(), vm["random"].as<int>());
		ptrHpg = rHyp.getHypergraphe();
	}

	if( vm.count("stat") ) {
		NewAlgorithm(statHpg, HyperGraphStat, ptrHpg);

		MotorAlgorithm::setAlgorithme( statHpg );
		MotorAlgorithm::runAlgorithme();

		boost::shared_ptr<HyperGraphStat> s = boost::static_pointer_cast<HyperGraphStat>( statHpg );

		std::cout << "Hyper-vertex : " << s->getNbrHyperVertex() << std::endl
				  << "Hyper-edge   : " << s->getNbrHyperEdge()   << std::endl
				  << "Nbr. links   : " << s->getNbrLinks()       << std::endl
				  << "Rang         : " << s->getRang()           << std::endl
				  << "Co-rang      : " << s->getCoRang()         << std::endl;

		return 0;
	}

	if( vm.count("dual") ) {

		NewAlgorithm(dualAlgo, Dual, ptrHpg);

		MotorAlgorithm::setAlgorithme( dualAlgo );
		MotorAlgorithm::runAlgorithme();

		RStructure r( dualAlgo->getResult() );

		WriterFile w(r.getHypergrapheResult());
		if( vm.count("adjacence") ) {
			w.writeAdjacentMatrix( std::cout );
		} else {
			w.writeHypergraph( std::cout );
		}

		return 0;
	}

	if( vm.count("kuniform") ) {
		boost::shared_ptr<AlgorithmeAbstrait> kuniformAlgo( new kUniform( ptrHpg, vm["kuniform"].as<int>() ) );
		MotorAlgorithm::setAlgorithme( kuniformAlgo );
		MotorAlgorithm::runAlgorithme();

		RStructure r( kuniformAlgo->getResult() );
		if( r.getBooleanResult() ) {
			std::cout << "L'hypergraphe est " << vm["kuniform"].as<int>() << "-uniforme." << std::endl;
		} else {
			std::cout << "L'hypergraphe n'est pas " << vm["kuniform"].as<int>() << "-uniforme." << std::endl;
		}

		return 0;
	}

	if( vm.count("linear") ) {

		NewAlgorithm(linearAlgo, Linear, ptrHpg);

		MotorAlgorithm::setAlgorithme( linearAlgo );
		MotorAlgorithm::runAlgorithme();

		RStructure r( linearAlgo->getResult() );
		if( r.getBooleanResult() ) {
			std::cout << "L'hypergraphe est Linéaire." << std::endl;
		} else {
			std::cout << "L'hypergraphe n'est pas Linéaire." << std::endl;
		}

		return 0;
	}

	if( vm.count("helly") ) {
		NewAlgorithm(hellyAlgo, Helly, ptrHpg);

		MotorAlgorithm::setAlgorithme( hellyAlgo );
		MotorAlgorithm::runAlgorithme();

		RStructure r( hellyAlgo->getResult() );
		if( r.getBooleanResult() ) {
			std::cout << "L'hypergraphe est Helly." << std::endl;
		} else {
			std::cout << "L'hypergraphe n'est pas Helly." << std::endl;
		}
		return 0;
	}

	if( vm.count("kregular") ) {
		NewAlgorithm(kregularAlgo, kRegular, ptrHpg);

		MotorAlgorithm::setAlgorithme( kregularAlgo );
		MotorAlgorithm::runAlgorithme();

		RStructure r( kregularAlgo->getResult() );
		if( r.getBooleanResult() ) {
			std::cout << "L'hypergraphe est k-regulier." << std::endl;
		} else {
			std::cout << "L'hypergraphe n'est pas k-regulier." << std::endl;
		}

		return 0;
	}

	if( vm.count("simple") ) {
		NewAlgorithm(simpleAlgo, Simple, ptrHpg);

		MotorAlgorithm::setAlgorithme( simpleAlgo );
		MotorAlgorithm::runAlgorithme();

		RStructure r( simpleAlgo->getResult() );
		if( r.getBooleanResult() ) {
			std::cout << "L'hypergraphe est simple." << std::endl;
		} else {
			std::cout << "L'hypergraphe n'est pas simple." << std::endl;
		}

		return 0;
	}

	if(vm.count("path") ) {
		boost::shared_ptr<Path> pathAlgo( new Path( ptrHpg ) );

		pathAlgo->setHyperVertex(
				ptrHpg->getHyperVertexById(vm["source"].as<int>()),
				ptrHpg->getHyperVertexById(vm["destination"].as<int>() )
			);

		boost::shared_ptr<AlgorithmeAbstrait> algoPathAbstrait( pathAlgo );

		MotorAlgorithm::setAlgorithme( algoPathAbstrait );
		MotorAlgorithm::runAlgorithme();

		RStructurePath r( pathAlgo->getPathResult() );

		for(unsigned int i=0; i<r.getPathResult()->size(); i++) {
			LibType::ListHyperVertex hvl( r.getPathResult()->at(i) );
			for(unsigned int j=0; j<hvl.size(); j++) {
				std::cout << hvl.at(j)->getIdentifier() << " ";
			}
			std::cout << std::endl;
		}

	}

	if( vm.count("connexe") ) {
		NewAlgorithm(algoConnected, Connected, ptrHpg);

		MotorAlgorithm::setAlgorithme( algoConnected );
		MotorAlgorithm::runAlgorithme();

		RStructure r( algoConnected->getResult() );
		if( r.getBooleanResult() ) {
			std::cout << "L'hypergraphe est connexe." << std::endl;
		} else {
			std::cout << "L'hypergraphe n'est pas connexe." << std::endl;
		}

		return 0;
	}

	if( vm.count("adjacence") ) {
		WriterFile w( ptrHpg );
		w.writeAdjacentMatrix( std::cout );
	} else {
		WriterFile w( ptrHpg );
		w.writeHypergraph( std::cout );
	}

	return 0;
}


/*    Copyright (c) 2010-2018, Delft University of Technology
 *    All rigths reserved
 *
 *    This file is part of the Tudat. Redistribution and use in source and
 *    binary forms, with or without modification, are permitted exclusively
 *    under the terms of the Modified BSD license. You should have received
 *    a copy of the license with this file. If not, please or visit:
 *    http://tudat.tudelft.nl/LICENSE.
 */

#include <map>
#include <string>
#include <iostream>

#include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/trim.hpp>
#include <boost/make_shared.hpp>
#include <boost/bind.hpp>

#include "Tudat/Astrodynamics/Aerodynamics/aerodynamicCoefficientInterface.h"
#include "Tudat/Astrodynamics/Aerodynamics/customAerodynamicCoefficientInterface.h"
#include "Tudat/SimulationSetup/EnvironmentSetup/createFlightConditions.h"

namespace tudat
{

namespace simulation_setup
{

//! Function to create an atmospheric flight conditions object
std::shared_ptr< aerodynamics::AtmosphericFlightConditions > createAtmosphericFlightConditions(
        const std::shared_ptr< Body > bodyWithFlightConditions,
        const std::shared_ptr< Body > centralBody,
        const std::string& nameOfBodyUndergoingAcceleration,
        const std::string& nameOfBodyExertingAcceleration,
        const std::function< double( ) > angleOfAttackFunction,
        const std::function< double( ) > angleOfSideslipFunction,
        const std::function< double( ) > bankAngleFunction,
        const std::function< void( const double ) > angleUpdateFunction )
{
    // Check whether all required environment models are set.
    if( centralBody->getAtmosphereModel( ) == nullptr )
    {
        throw std::runtime_error(
                    "Error when making flight conditions, body " + nameOfBodyExertingAcceleration +
                    " has no atmosphere model." );
    }

    if( centralBody->getShapeModel( ) == nullptr )
    {
        throw std::runtime_error(
                    "Error when making flight conditions, body " + nameOfBodyExertingAcceleration +
                    " has no shape model." );
    }

    if( centralBody->getRotationalEphemeris( ) == nullptr )
    {
        throw std::runtime_error(
                    "Error when making flight conditions, body " + nameOfBodyExertingAcceleration +
                    " has no rotation model." );
    }

    if( bodyWithFlightConditions->getAerodynamicCoefficientInterface( ) == nullptr )
    {
        throw std::runtime_error(
                    "Error when making flight conditions, body " + nameOfBodyUndergoingAcceleration +
                    " has no aerodynamic coefficients." );
    }

    // Create function to rotate state from intertial to body-fixed frame.
    std::function< Eigen::Quaterniond( ) > rotationToFrameFunction =
            std::bind( &Body::getCurrentRotationToLocalFrame, centralBody );
    std::function< Eigen::Matrix3d( ) > rotationMatrixToFrameDerivativeFunction =
            std::bind( &Body::getCurrentRotationMatrixDerivativeToLocalFrame, centralBody );

    std::function< Eigen::Matrix< double, 6, 1 >( ) > bodyStateFunction = std::bind( &Body::getState, bodyWithFlightConditions );
    std::function< Eigen::Matrix< double, 6, 1 >( ) > centralBodyStateFunction = std::bind( &Body::getState, centralBody );

    std::function< Eigen::Matrix< double, 6, 1 >( ) > relativeBodyFixedStateFunction =
            std::bind( &ephemerides::transformRelativeStateToFrame< double >,
                         bodyStateFunction, centralBodyStateFunction,
                         rotationToFrameFunction,
                         rotationMatrixToFrameDerivativeFunction );

    // Create aerodynamic angles calculator and set in flight conditions.
    std::shared_ptr< reference_frames::AerodynamicAngleCalculator > aerodynamicAngleCalculator =
            std::make_shared< reference_frames::AerodynamicAngleCalculator >(
                relativeBodyFixedStateFunction,
                std::bind( &simulation_setup::Body::getCurrentRotationToGlobalFrame, centralBody ),
                nameOfBodyExertingAcceleration, 1,
                angleOfAttackFunction, angleOfSideslipFunction, bankAngleFunction, angleUpdateFunction );

    // Add wind model if present
    if( centralBody->getAtmosphereModel( )->getWindModel( ) != nullptr )
    {
        if( centralBody->getShapeModel( ) == nullptr )
        {
            std::cerr << "Warnning, body " << nameOfBodyExertingAcceleration << " has wind model, but no shape model, cannot compute wind as function of altitude " << std::endl;
        }
        else
        {
           aerodynamicAngleCalculator->setWindModel(
                        centralBody->getAtmosphereModel( )->getWindModel( ), centralBody->getShapeModel( ) );
        }
    }

    // Create flight conditions.
    std::function< double( const std::string& )> controlSurfaceDeflectionFunction;
    if( bodyWithFlightConditions->getVehicleSystems( ) != nullptr )
    {
        controlSurfaceDeflectionFunction = std::bind(
                    &system_models::VehicleSystems::getCurrentControlSurfaceDeflection,
                    bodyWithFlightConditions->getVehicleSystems( ), std::placeholders::_1 );
    }
    std::shared_ptr< aerodynamics::AtmosphericFlightConditions > flightConditions =
            std::make_shared< aerodynamics::AtmosphericFlightConditions >(
                centralBody->getAtmosphereModel( ), centralBody->getShapeModel( ),
                bodyWithFlightConditions->getAerodynamicCoefficientInterface( ), aerodynamicAngleCalculator,
                controlSurfaceDeflectionFunction );

    return flightConditions;

}

//! Function to create a flight conditions object
std::shared_ptr< aerodynamics::FlightConditions >  createFlightConditions(
        const std::shared_ptr< Body > bodyWithFlightConditions,
        const std::shared_ptr< Body > centralBody,
        const std::string& nameOfBodyUndergoingAcceleration,
        const std::string& nameOfBodyExertingAcceleration )
{
    // Check whether all required environment models are set.
    if( centralBody->getShapeModel( ) == nullptr )
    {
        throw std::runtime_error(
                    "Error when making flight conditions, body " + nameOfBodyExertingAcceleration +
                    " has no shape model." );
    }

    if( centralBody->getRotationalEphemeris( ) == nullptr )
    {
        throw std::runtime_error(
                    "Error when making flight conditions, body " + nameOfBodyExertingAcceleration +
                    " has no rotation model." );
    }

    // Create function to rotate state from intertial to body-fixed frame.
    std::function< Eigen::Quaterniond( ) > rotationToFrameFunction =
            std::bind( &Body::getCurrentRotationToLocalFrame, centralBody );
    std::function< Eigen::Matrix3d( ) > rotationMatrixToFrameDerivativeFunction =
            std::bind( &Body::getCurrentRotationMatrixDerivativeToLocalFrame, centralBody );

    std::function< Eigen::Matrix< double, 6, 1 >( ) > bodyStateFunction = std::bind( &Body::getState, bodyWithFlightConditions );
    std::function< Eigen::Matrix< double, 6, 1 >( ) > centralBodyStateFunction = std::bind( &Body::getState, centralBody );

    std::function< Eigen::Matrix< double, 6, 1 >( ) > relativeBodyFixedStateFunction =
            std::bind( &ephemerides::transformRelativeStateToFrame< double >,
                         bodyStateFunction, centralBodyStateFunction,
                         rotationToFrameFunction,
                         rotationMatrixToFrameDerivativeFunction );

    // Create aerodynamic angles calculator and set in flight conditions.
    std::shared_ptr< reference_frames::AerodynamicAngleCalculator > aerodynamicAngleCalculator =
            std::make_shared< reference_frames::AerodynamicAngleCalculator >(
                relativeBodyFixedStateFunction,
                std::bind( &simulation_setup::Body::getCurrentRotationToGlobalFrame, centralBody ),
                nameOfBodyExertingAcceleration, 1 );

    return std::make_shared< aerodynamics::FlightConditions >(
                centralBody->getShapeModel( ), aerodynamicAngleCalculator );

}

//! Function to set the angle of attack to trimmed conditions.
std::shared_ptr< aerodynamics::TrimOrientationCalculator > setTrimmedConditions(
        const std::shared_ptr< aerodynamics::AtmosphericFlightConditions > flightConditions )
{
    // Create trim object.
    std::shared_ptr< aerodynamics::TrimOrientationCalculator > trimOrientation =
            std::make_shared< aerodynamics::TrimOrientationCalculator >(
                flightConditions->getAerodynamicCoefficientInterface( ) );

    // Create angle-of-attack function from trim object.
    std::function< std::vector< double >( ) > untrimmedIndependentVariablesFunction =
            std::bind( &aerodynamics::AtmosphericFlightConditions::getAerodynamicCoefficientIndependentVariables,
                         flightConditions );
    std::function< std::map< std::string, std::vector< double > >( ) > untrimmedControlSurfaceIndependentVariableFunction =
            std::bind( &aerodynamics::AtmosphericFlightConditions::getControlSurfaceAerodynamicCoefficientIndependentVariables,
                         flightConditions );

    flightConditions->getAerodynamicAngleCalculator( )->setOrientationAngleFunctions(
                std::bind( &aerodynamics::TrimOrientationCalculator::findTrimAngleOfAttackFromFunction, trimOrientation,
                             untrimmedIndependentVariablesFunction, untrimmedControlSurfaceIndependentVariableFunction ) );

    return trimOrientation;
}

//! Function to set the angle of attack to trimmed conditions.
std::shared_ptr< aerodynamics::TrimOrientationCalculator > setTrimmedConditions(
        const std::shared_ptr< Body > bodyWithFlightConditions )
{
    if( std::dynamic_pointer_cast< aerodynamics::AtmosphericFlightConditions >(
                bodyWithFlightConditions->getFlightConditions( ) ) == nullptr )
    {
        throw std::runtime_error( "Error, body does not have FlightConditions when setting trim conditions." );
    }

    return setTrimmedConditions( std::dynamic_pointer_cast< aerodynamics::AtmosphericFlightConditions >(
                                     bodyWithFlightConditions->getFlightConditions( ) ));
}

//! Function that must be called to link the AerodynamicGuidance object to the simulation
void setGuidanceAnglesFunctions(
        const std::shared_ptr< aerodynamics::AerodynamicGuidance > aerodynamicGuidance,
        const std::shared_ptr< reference_frames::AerodynamicAngleCalculator > angleCalculator )
{
    angleCalculator->setOrientationAngleFunctions(
                std::bind( &aerodynamics::AerodynamicGuidance::getCurrentAngleOfAttack, aerodynamicGuidance ),
                std::bind( &aerodynamics::AerodynamicGuidance::getCurrentAngleOfSideslip, aerodynamicGuidance ),
                std::bind( &aerodynamics::AerodynamicGuidance::getCurrentBankAngle, aerodynamicGuidance ),
                std::bind( &aerodynamics::AerodynamicGuidance::updateGuidance, aerodynamicGuidance, std::placeholders::_1 ) );
}

//! Function that must be called to link the AerodynamicGuidance object to the simulation
void setGuidanceAnglesFunctions(
        const std::shared_ptr< aerodynamics::AerodynamicGuidance > aerodynamicGuidance,
        const std::shared_ptr< simulation_setup::Body > bodyWithAngles )
{
    std::shared_ptr< reference_frames::DependentOrientationCalculator >  orientationCalculator =
            bodyWithAngles->getDependentOrientationCalculator( );
    std::shared_ptr< reference_frames::AerodynamicAngleCalculator > angleCalculator =
            std::dynamic_pointer_cast< reference_frames::AerodynamicAngleCalculator >( orientationCalculator );

    if( angleCalculator == nullptr )
    {
        throw std::runtime_error( "Error, body does not have AerodynamicAngleCalculator when setting aerodynamic guidance" );
    }
    else
    {
        setGuidanceAnglesFunctions( aerodynamicGuidance, angleCalculator );
    }
}

} // namespace simulation_setup

} // namespace tudat