// This is an independent project of an individual developer. Dear PVS-Studio, please check it.
// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com

/**
 * File:   PnlAction.cpp
 * Author: kan
 *
 * Created on 2015.11.11
 * @lastupdate 2018.05.18
 */

#include <map>
#include <list>
#include <vector>
#include <set>
#include <cassert>
#include <cmath>
#include <algorithm>
#include <cassert>
#include <numeric>
#include <climits>
#include <stdlib.h>     /* srand, rand */
#include <time.h>       /* time */
#include <boost/math/distributions/students_t.hpp>

#include "DelphisRound.h"
#include "Comparers.h"

//#define FULLDATA
#ifdef FULLDATA
    #include <iostream>
    #include "Indicators.h"
#endif

#include "PnlAction.h"

//------------------------------------------------------------------------------------------
TPriceSeries DealsToPnLs( const TDeals & aDeals ) {

    //<editor-fold desc="Удалить все пустые сделки">
    TDeals lDeals( aDeals );
    auto it = lDeals.begin();
    while( it != lDeals.end() ) {
        if( isZero( it->ClosePrice - it->OpenPrice ) ) {
            it = lDeals.erase( it );
        } else {
            ++it;
        }
    }
    //</editor-fold>

    TPriceSeries lPnLs; //( lDeals.size() );
    lPnLs.reserve(lDeals.size());

    //<editor-fold desc="Собственно конвертация">
    for( const TDeal& lDeal : lDeals ) {
        lPnLs.emplace_back(
            lDeal.CloseTime,
            ( (lDeal.DealSide == TDealSide::Buy) ? (lDeal.ClosePrice - lDeal.OpenPrice) : (lDeal.OpenPrice - lDeal.ClosePrice) ),
            ToDouble( lDeal.Volume )
        );
    }
    //</editor-fold>

    return lPnLs;
}

//------------------------------------------------------------------------------------------
bool CalcDrawDown(
    const TPriceSeries & aPnL,
    TPrice & aoMaxDD,
    TInnerDate & aoBegin,
    TInnerDate & aoReturn ) {

    aoBegin = -1;
    aoReturn = -1;

    if( aPnL.empty() ) {
        aoMaxDD = -1;
        return false;
    }

    TPrice lMax = 0.0;
    TPrice lMin = 0.0;
    TPrice lPnl = 0.0;
    TPrice lDelta = 0.0;
    size_t lBegin = 0;
    bool lHaveMin = false;
    bool lResult = false;

    for( size_t i = 0; i < aPnL.size() ; ++i ) {
        lPnl += aPnL[i].Price;

        if( IsGreat( lPnl, lMax ) ) { //очередной максимум
            lMax = lPnl;
            lMin = lPnl;

            if( lHaveMin ) {
                    aoBegin = aPnL[ lBegin ].DateTime;
                    aoReturn = aPnL[ i ].DateTime;
                    lHaveMin = false;
                    lResult = true;
            }

            lBegin = i;

        } else if( IsGreat( lMin, lPnl ) ) { //новый минимум
            lMin = lPnl;

            if( IsGreat( lMax - lMin, lDelta ) ) {
                lDelta = lMax - lMin;
                lHaveMin = true;
            }
        }
    }

    aoMaxDD = lDelta;

    return lResult;
}

//------------------------------------------------------------------------------------------
TPriceSeries ReductionOfTheIncome(
    const TPriceSeries & aPnL,
    const size_t aProfitNum,
    const double aProfitCoef,
    const size_t aLossNum,
    const double aLossCoef ) {

    std::multimap< TPrice, size_t > lProfits;
    for( size_t i = 0; i < aPnL.size(); ++i ) {

        TPrice lValue = aPnL[ i ].Price ;
        //lProfits.insert( std::pair< TPrice, size_t >( lValue, i ) ) ;
        lProfits.emplace( lValue, i ) ;
    }

    TPriceSeries lPnLVector( aPnL );
    size_t i = aProfitNum;
    for( auto it = lProfits.rbegin(); it != lProfits.rend(); ++it ) {
        if( it->first > 0 ) {
            const size_t lRealID = it->second ;
            TSimpleTick lTick = aPnL[ lRealID ] ;
            lTick.Price  /= aProfitCoef ;
            lPnLVector[ lRealID ] = lTick;
        }
        if( --i == 0 ) break;
    }

    i = aLossNum;
    for( auto it = lProfits.begin(); it != lProfits.end() ;++it ) {
        if( it->first < 0 ) {
            const size_t lRealID = it->second ;
            TSimpleTick lTick = aPnL[ lRealID ] ;
            lTick.Price  *= aLossCoef ;
            lPnLVector[ lRealID ] = lTick;
        }
        if( --i == 0 ) break;
    }

    return lPnLVector;
}

//------------------------------------------------------------------------------------------
TPrice PnLsToMoneyResult( const TPriceSeries & aPnls, const bool aUseVolume ) {
    TPrice lResult = 0.0;
    for( const TSimpleTick& lPnl : aPnls ) {
        lResult += lPnl.Price * ( aUseVolume ? lPnl.Volume : 1.0 );
    }

    return lResult;
}

//------------------------------------------------------------------------------------------
double Student_t_value( const size_t Sn, const double tail=0.2 ){//80%
    using boost::math::students_t;
    students_t dist( ToDouble(Sn) - 1 );
    const double T = quantile(complement(dist, tail / 2.0));

    return T;
}

//------------------------------------------------------------------------------------------
TPrice PnLsToMoneyStatValue( const TPriceSeries & aPnl, const bool aUseVolume, const size_t N, const double aQuantile ) {
    const size_t lSize = aPnl.size();
    if( lSize < 2 or N < 2 ) {
        return 0.0;
    }

    std::vector< TPrice > lPnl( lSize );
    TPrice sum = 0.0;
    for( size_t i=0; i<lSize; ++i ){
        const TPrice lValue = aPnl[i].Price * ( aUseVolume ? aPnl[i].Volume : 1.0 );
        sum += lValue;
        lPnl[i] = lValue;
    }

    const TPrice mean = sum / ToDouble(lSize);

    TPrice lstdev = 0.0;
    for( size_t i=0; i<lSize; ++i ){
        lstdev += pow(lPnl[i] - mean, 2);
    }

    lstdev /= ToDouble(lSize);

    const double lStudAN = Student_t_value( lSize, aQuantile ) / sqrt( ToDouble(N) );

    return (mean - sqrt(lstdev)*lStudAN)*ToDouble(N);
}

//------------------------------------------------------------------------------------------
TPrice PnLsToMoneyStatValueGost( const TPriceSeries & aPnl, const bool aUseVolume, const size_t N ) {
    const size_t lSize = aPnl.size();
    if( lSize < 2 ) {
        return 0.0;
    }

    std::vector< TPrice > lPnl( lSize );
    TPrice sum = 0.0;
    for( size_t i=0; i<lSize; ++i ){
        const TPrice lValue = aPnl[i].Price * ( aUseVolume ? aPnl[i].Volume : 1.0 );
        sum += lValue;
        lPnl[i] = lValue;
    }

    const TPrice mean = sum / ToDouble(lSize);

    TPrice lstdev = 0.0;
    for( size_t i=0; i<lSize; ++i ){
        lstdev += pow(lPnl[i] - mean, 2);
    }

    lstdev /= (ToDouble(lSize) - 1.5);//ГОСТ Р 8.736-2011

    return ( mean - sqrt( lstdev ) / sqrt( ToDouble(lSize) ) )*ToDouble(N);
}

//------------------------------------------------------------------------------------------
TPrice PnLsToMoneyMonteCarlo( const TPriceSeries & aPnl, const bool aUseVolume, const size_t N, const size_t aSamples ){
    const size_t lSize = aPnl.size();
    if( lSize < 2 or N < 2 ) {
        return 0.0;
    }

    TPrice lResult = ToDouble( ULLONG_MAX ); //очень большое число

    for( size_t i=0; i<aSamples; ++i ){
        TPrice lPnlTest = 0.0;
        for( size_t j=0; j<N; ++j ){
            const size_t lID = rand() % lSize;
            const TPrice lPnlSample = aPnl[lID].Price * (aUseVolume ? aPnl[lID].Volume : 1.0);
            lPnlTest += lPnlSample;
        }
        lPnlTest /= ToDouble(N);

        lResult = std::min(lResult, lPnlTest);
    }

    return lResult * ToDouble(N);
}

//------------------------------------------------------------------------------------------
TPrice PnLsToMoneyMonteCarloQuantile( const TPriceSeries & aPnl, const bool aUseVolume, const size_t N, const size_t aSamples, const double aQuantile ) {
    const size_t lSize = aPnl.size();
    if( lSize < 2 or N < 2 or IsGreat( aQuantile, 1.0 ) ) {
        return 0.0;
    }

    std::multiset<TPrice> lData;

    for( size_t i=0; i<aSamples; ++i ){
        TPrice lPnlTest = 0.0;
        for( size_t j=0; j<N; ++j ){
            const size_t lID = rand() % lSize;
            const TPrice lPnlSample = aPnl[lID].Price * (aUseVolume ? aPnl[lID].Volume : 1.0);
            lPnlTest += lPnlSample;
        }
        lPnlTest /= ToDouble(N);

        lData.insert( lPnlTest );
    }

    const size_t lID = RoundToSize_t( aQuantile * ToDouble( aSamples ) );

    auto iter = lData.cbegin();
    std::advance(iter, lID);

    const TPrice lResult = *iter;

    return lResult * ToDouble(N);
}

//------------------------------------------------------------------------------------------
TPrice DealsToPNLCoefficientQuick(
    const TDeals & aDeals,
    const TPrice aFirstPrice,
    const size_t aMinDeals ) {

    TPriceSeries lPnL( DealsToPnLs( aDeals ) );
    if( lPnL.size() <= aMinDeals ) {
        return gMaxBedAttraction;
    }

    TPrice lMaxDD = -1;
    TInnerDate lBegin;
    TInnerDate lReturn;

    bool lDDOk = CalcDrawDown( lPnL, lMaxDD, lBegin, lReturn );
    bool lGoodResult = lDDOk or ( isZero( lMaxDD ) );

    if( not lGoodResult ) { return gMaxBedAttraction; }

    const size_t lDealsToReduction = std::max( std::min( aMinDeals / 2, 3UL ), 1UL );
    const double lReductionCoeff = 2.0;
    const TPriceSeries lPnLPes( ReductionOfTheIncome( lPnL, lDealsToReduction, lReductionCoeff, lDealsToReduction, lReductionCoeff ) );
    const TPrice lPnlMoneyReduction = PnLsToMoneyResult( lPnLPes );

    const TPrice lPnlMoney = PnLsToMoneyResult( lPnL );
    const TPrice lResult =  lPnlMoneyReduction / ( lMaxDD + std::abs( lPnlMoney ) );
//    TPrice lResult =  lPnlMoney / ( lMaxDD + std::abs( lPnlMoney ) );
//    TPrice lResult =  lPnlMoney / ( lMaxDD + 1 );

    return lResult ;
}

//------------------------------------------------------------------------------------------
TPrice DealsToPNLCoefficient(
    const TDeals & aDeals,
    const TInnerDate aFirstPoint,
    const TInnerDate aLastPoint,
    const TPrice aMinPnl,
    const size_t aMinDeals,
    const size_t aQuantTime ) {

    if( aDeals.size() < aMinDeals ) {
        return gMaxBedAttraction;
    }

    //region перевести список сделок в список d_pnl
    TPrice lResult = gMaxBedAttraction ;
    TPrice lPnLValue = 0.0;
    //TInnerDate lPnLTime = 0.0;
    std::list< TSimpleTick > lPnLs;
    lPnLs.push_back( {aFirstPoint, 0.0, 0} );

    for( const auto &lDeal : aDeals ) {
        TInnerDate lEmptyDate = lDeal.OpenTime;
        TSimpleTick lEmptyTick = { lEmptyDate, 0.0, 0 };
        lPnLs.push_back( lEmptyTick );

        TInnerDate lBeginDate = lDeal.CloseTime;

        const TPrice lDealPnl =
            (lDeal.DealSide == TDealSide::Buy) ?
                (lDeal.ClosePrice - lDeal.OpenPrice) :
                (lDeal.OpenPrice - lDeal.ClosePrice);

        TSimpleTick lRealTick = { lBeginDate, lDealPnl, 1 };
        lPnLs.push_back( lRealTick );

        lPnLValue += lDealPnl;
        //lPnLTime += ( lDeal.CloseTime - lDeal.OpenTime );
    }

    lPnLs.push_back( {aLastPoint, 0.0, 0} );
    //endregion

    // рассчитать общий pnl, учесть aMinPnl
    if( lPnLValue <= aMinPnl ) {
        return gMaxBedAttraction;
    }

    //region  рассчитать ср. и макс. d_pnl, число сделок
    const size_t lWeekCouner = CeilToSize_t( ( aLastPoint - aFirstPoint ) / static_cast< double >( aQuantTime ) );
    std::vector< TPrice > lQuantPnL( lWeekCouner );
    size_t lIdx=0;
    TInnerDate lNextDate = aFirstPoint + static_cast<TInnerDate>( aQuantTime );

    std::list< TSimpleTick > lPnLsToQuanting( lPnLs );
    while( not lPnLsToQuanting.empty() ) {
        auto lTick = lPnLsToQuanting.front();
        lPnLsToQuanting.pop_front();

        if( lTick.DateTime > lNextDate ) {
            lNextDate += static_cast<TInnerDate>( aQuantTime );
            lIdx++;
        }
        lQuantPnL[ lIdx ] += lTick.Price;
    }

    TPrice lMaxPnl = 0.0; //если lPnLValue>0 значит точно есть положительный pnl за интервал
    TPrice lMidPnl = 0.0;
    size_t lDealCounter = 0 ;
    for( const auto ldPnl : lQuantPnL ) {
        if( ldPnl > lMaxPnl ){ lMaxPnl = ldPnl; }

        lMidPnl += ldPnl ;
        lDealCounter++ ;
    }
    //endregion

    // ... рассчитать разброс pnl,
    TPrice lPnLValatility = ( lMidPnl / ToDouble( lDealCounter ) ) / lMaxPnl;

    //region расчитать d_DD, учесть DD==0
    TInnerDate lDD_begin ;
    TPrice lCurrDD = 0.0;
    TPrice lSumCurrDD = 0.0;
    TInnerDate lPrioreDate = aFirstPoint ;
    for( const auto& lTick : lPnLs ) {

        if( isZero( lCurrDD ) and lTick.Price < 0.0 ) {

            lCurrDD = lTick.Price;
            lDD_begin = lPrioreDate ;

            lSumCurrDD += lCurrDD * ( lTick.DateTime - lDD_begin ) ;// 86400.0;

        } else if( lCurrDD < 0.0 ) {
            TPrice lPriorDD = lCurrDD;
            lCurrDD += lTick.Price;

            if( lCurrDD > 0.0 ) {
                lCurrDD = 0.0;

            } else {
                lPriorDD = lCurrDD;
            }

            lSumCurrDD += lPriorDD * ( lTick.DateTime - lPrioreDate ) ;// 86400.0;
        }

        lPrioreDate = lTick.DateTime ;
    }
    //endregion

    if( isPositiveValue( lSumCurrDD ) ) {
        return gMaxBedAttraction;
    }

    // ... рассчитать ср.DD
    const TPrice lTW_DD = -lSumCurrDD / ( aLastPoint - aFirstPoint );

    // Расчитать коэффициент            Это среднеинтервальный заработок
    lResult = lPnLValatility / ( lTW_DD + 1.0 ) * lMidPnl /  ToDouble( lDealCounter ) ; //lPnLValue / lPnLTime ;

    return lResult ;
}

//------------------------------------------------------------------------------------------
TPrice DealsToCoeff(
    const TBarSeries & aBars,
    const TDeals & aDeals,
    const size_t aMinDeals,
    TPrice & aoPnl,
    TPrice & aoMaxDD,
    size_t & aoMaxPos,
    size_t & aoMeadPos ) {
    #ifdef FULLDATA
        std::cout << "aDeals.size() = " << aDeals.size() << std::endl;
    #endif

    if( aDeals.size() <= aMinDeals ) {
        aoPnl = GetBadPrice();
        aoMaxDD = GetBadPrice();
        aoMaxPos = 0;
        aoMeadPos = 0;
        return gMaxBedAttraction;
    }

    TPriceSeries lBalance( aBars.size() );

    for( size_t i=0; i<aBars.size(); ++i ){
        lBalance[i]={aBars[i].DateTime,0.0,0.0};
    }

    auto lTickCompare = []( const TSimpleTick& aLeft, const TSimpleTick& aRigth )->bool {
        return aLeft.DateTime < aRigth.DateTime;
    };

    #ifdef FULLDATA
        std::cout << "\nDeals" << std::endl;
    #endif

    for( auto it=aDeals.begin(); it!=aDeals.end(); ++it ) {

        const TInnerDate lOpenTime = it->OpenTime;
        const TInnerDate lCloseTime = it->CloseTime;
        const TPrice lOpenPrice = it->OpenPrice;
        const TPrice lClosePrice = it->ClosePrice;

        auto itBegin = std::lower_bound (
            lBalance.begin(),
            lBalance.end(),
            TSimpleTick{ lOpenTime,0.0,0.0 },
            lTickCompare
        );
        assert( itBegin != lBalance.end() );

        auto itEnd = std::upper_bound(
            itBegin,
            lBalance.end(),
            TSimpleTick{ lCloseTime,0.0,0.0 },
            lTickCompare
        );

        TPrice lBalanceVolume = 0.0;

        if( it->DealSide == TDealSide::Buy ) {
            for( auto itBalance=itBegin; itBalance != std::prev( itEnd ) ; ++itBalance ){
                itBalance->Volume += 1.0;
                itBalance->Price -= lOpenPrice;
            }
            lBalanceVolume = lClosePrice - lOpenPrice ;

        } else {
            for( auto itBalance=itBegin; itBalance != std::prev( itEnd ) ; ++itBalance ) {
                itBalance->Volume -= 1.0;
                itBalance->Price += lOpenPrice;
            }
            lBalanceVolume = lOpenPrice - lClosePrice;
        }

        for( auto itBalance=std::prev( itEnd ); itBalance != lBalance.end(); ++itBalance ) {
            itBalance->Price += lBalanceVolume;
        }
    }

    if( not isZero( lBalance.rbegin()->Volume ) ) { ///\todo вообще-то нужно учесть сторону для каждой конкретной сделки
        const TPrice lClosePrice = aBars.rbegin()->Close;
        const TPrice lBalancedSide = ( aDeals.rbegin()->DealSide == TDealSide::Buy ) ? 1.0 : -1.0;
        lBalance.rbegin()->Price += lBalancedSide * lBalance.rbegin()->Volume * lClosePrice;
        lBalance.rbegin()->Volume = 0;
    }

    TPrice lMax = 0.0;
    TPrice lMin = 0.0;
    aoPnl = 0.0;
    aoMaxDD = 0.0;

    #ifdef FULLDATA
    std::cout << "\nDD" << std::endl;
    #endif

    std::vector< size_t > lPosition( aBars.size(), 0 );

    for( size_t i=0; i<aBars.size(); ++i ) {
        lPosition[ i ] = ToSize_t( std::fabs( lBalance[i].Volume ) );
        aoPnl = lBalance[i].Price + aBars[i].Close * lBalance[i].Volume;

        if( IsGreat( aoPnl, lMax ) ) { //очередной максимум
            lMax = aoPnl;
            lMin = aoPnl;

        } else if( IsGreat( lMin, aoPnl ) ) { //новый минимум
            lMin = aoPnl;
            const TPrice lCurrentDD = lMax - lMin;
            if( IsGreat( lCurrentDD, aoMaxDD ) ) {
                aoMaxDD = lCurrentDD;
            }
        }
        #ifdef FULLDATA
        std::cout << RoundToSize_t( aBars[i].DateTime ) << " " << aBars[i].Close << " " << lBalance[i].Volume << " " << aoPnl << " " << lMax << " " << lMin << " " << aoMaxDD << std::endl;
        #endif
    }

    std::sort( lPosition.begin(), lPosition.end() );
    aoMaxPos = *lPosition.rbegin();
    aoMeadPos = lPosition[ lPosition.size() / 2 ];

    const TPrice lResult = isZero( std::abs( aoPnl ) + aoMaxDD ) ? gMaxBedAttraction : (aoPnl / ( std::abs( aoPnl ) + aoMaxDD )) ;
    #ifdef FULLDATA
    std::cout << "lResult = " << lResult << std::endl;
    #endif

    return lResult;
}

//------------------------------------------------------------------------------------------
TDoubles ToDoublesArray( const TPriceSeries & aPriceSeries ) {
    const size_t lArraySize = aPriceSeries.size();
    TDoubles lResult( lArraySize );

    for( size_t i=0; i<lArraySize; ++i ) {
        lResult[i]=aPriceSeries[i].Price;
    }

    return lResult;
}

//------------------------------------------------------------------------------------------
TPriceSeries PnlsToDaily( const TPriceSeries & aPnls ) {

    if( aPnls.empty() ) {
        return TPriceSeries();
    }
    TInnerDate lMinDate = gMaxInteger;
    TInnerDate lMaxDate = 0;

    for( const auto &lDeal : aPnls ) {
        const TInnerDate lDealDate = Trunc( lDeal.DateTime / gOneDay );
        lMinDate = IsLess( lMinDate, lDealDate ) ? lMinDate : lDealDate;
        lMaxDate = IsGreat( lMaxDate, lDealDate ) ? lMaxDate : lDealDate;
    }

    TPriceSeries lResult( ToSize_t( lMaxDate-lMinDate+1 ) );
    for( const auto &lDeal : aPnls ) {
        const size_t lDealDate = ToSize_t( Trunc( lDeal.DateTime / gOneDay ) - lMinDate );
        const TPrice lDealPnl = lDeal.Price * ((lDeal.Volume == 0) ? 1.0 : lDeal.Volume );

        TSimpleTick lDayDeal = lResult[ lDealDate ];
        lDayDeal.DateTime = ToDouble( Trunc( lDeal.DateTime / gOneDay ) * gOneDay );
        lDayDeal.Price += lDealPnl;
        lDayDeal.Volume += 1.0;

        lResult[ lDealDate ] = lDayDeal;
    }

    for( size_t i = 1; i < lResult.size(); ++i ){
        lResult[i].Price += lResult[i-1].Price;
    }

    return lResult;
}

//------------------------------------------------------------------------------------------
bool IsGrows( const TPriceSeries & aDailyPnls, const size_t aPeriod, const size_t aTollerance ) {
    if( aPeriod == 0 ){
        throw std::logic_error( "aPeriod can be positive" );
    }

    if( aDailyPnls.size() <= aPeriod ) {
        return false;
    }

    size_t lTollerance = ( aTollerance == 0 ) ? 1 : aTollerance;

    for( size_t i=0; i < aDailyPnls.size() - aPeriod; ++i ) {
        if( IsGreat( aDailyPnls[i].Price, aDailyPnls[ i+aPeriod ].Price ) ) {
            if( --lTollerance == 0 ) {
                return false;
            }
        }
    }

    return true;
}

//------------------------------------------------------------------------------------------
TPriceSeries PnLsAmplifier( const TPriceSeries &aPnl, const std::vector<double> &aAmplifiers, const TInnerDate aBegin, const TInnerDate aEnd ) {

    if( aAmplifiers.size() < 2UL ){
        return aPnl;
    }

    //sort deals
    TPriceSeries lPnls( aPnl );
    std::sort( lPnls.begin(), lPnls.end(),
        []( const TSimpleTick& lh, const TSimpleTick& rh ){
            return IsLess( lh.DateTime, rh.DateTime );
        }
    );

    //days range detect
    const TInnerDate lMinDate =  trunc( (isPositiveValue( aBegin ) ? aBegin : lPnls.begin()->DateTime) / gOneDay );
    const TInnerDate lMaxDate =  trunc( (isPositiveValue( aEnd ) ? aEnd : lPnls.rbegin()->DateTime) / gOneDay );

    if( CeilToSize_t(lMaxDate-lMinDate) < aAmplifiers.size() ){
        return aPnl;
    }

    const double lMinDelta = trunc( (lMaxDate-lMinDate) / ToDouble( aAmplifiers.size() ) );
    if( IsLess( lMinDelta, 1.0 ) ){
        return aPnl;
    }

    std::vector<TInnerDate> ldates( aAmplifiers.size() );
    for( size_t i=0; i<aAmplifiers.size(); ++i ) {
        ldates[i] = lMinDate + lMinDelta * ToDouble(i+1) ;
    }

    TPriceSeries lPnls_result;
    lPnls_result.reserve( ToSize_t(*aAmplifiers.rbegin()) * aPnl.size() );
    for( auto it = lPnls.begin(); it != lPnls.end(); ++it ){
        const TSimpleTick lTick( *it );

        for( size_t i=0; i < ldates.size(); ++i ){
            if( not IsGreat( trunc(lTick.DateTime / gOneDay), ldates[i] ) ){
                for( size_t j=0; j < ToSize_t(aAmplifiers[i]); ++j ){
                    lPnls_result.push_back( lTick );
                }
                break;
            }
        }
    }

    lPnls_result.shrink_to_fit();
    return lPnls_result;
}

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


// http://turtle.sourceforge.net
//
// Copyright Mathieu Champlon 2008
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)

#ifndef MOCK_CONSTRAINTS_HPP_INCLUDED
#define MOCK_CONSTRAINTS_HPP_INCLUDED

#include "config.hpp"
#include "constraint.hpp"
#include "detail/addressof.hpp"
#include "detail/move_helper.hpp"
#include <boost/ref.hpp>
#include <boost/version.hpp>
#include <boost/utility/enable_if.hpp>
#include <boost/type_traits/common_type.hpp>
#include <boost/type_traits/is_convertible.hpp>
#include <boost/type_traits/has_equal_to.hpp>
#if BOOST_VERSION >= 107000
#include <boost/test/tools/floating_point_comparison.hpp>
#else
#include <boost/test/floating_point_comparison.hpp>
#endif

namespace mock
{
    MOCK_UNARY_CONSTRAINT( any, 0,, ((void)actual, true) )
    MOCK_UNARY_CONSTRAINT( affirm, 0,, !! actual )
    MOCK_UNARY_CONSTRAINT( negate, 0,, ! actual )
    MOCK_UNARY_CONSTRAINT( evaluate, 0,, actual() )

    MOCK_NARY_CONSTRAINT( less, 1, ( expected ), actual < expected )
    MOCK_NARY_CONSTRAINT( greater, 1, ( expected ), actual > expected )
    MOCK_NARY_CONSTRAINT( less_equal, 1, ( expected ), actual <= expected )
    MOCK_NARY_CONSTRAINT( greater_equal, 1, ( expected ), actual >= expected )

#if BOOST_VERSION < 105900

#   define MOCK_SMALL() \
        boost::test_tools::check_is_small( actual, tolerance )
#   define MOCK_PERCENT_TOLERANCE() \
        boost::test_tools::check_is_close( \
            actual, \
            expected, \
            boost::test_tools::percent_tolerance( tolerance ) )
#   define MOCK_FRACTION_TOLERANCE() \
        boost::test_tools::check_is_close( \
            actual, \
            expected, \
            boost::test_tools::fraction_tolerance( tolerance ) )

#else // BOOST_VERSION < 105900

namespace detail
{
    template< typename T, typename Tolerance >
    bool is_small( const T& t, const Tolerance& tolerance )
    {
        return boost::math::fpc::small_with_tolerance< T >( tolerance )( t );
    }

    template< typename T1, typename T2, typename Tolerance >
    bool is_close( const T1& t1, const T2& t2, const Tolerance& tolerance )
    {
        typedef typename boost::common_type< T1, T2 >::type common_type;
        return boost::math::fpc::close_at_tolerance< common_type >(
            tolerance, boost::math::fpc::FPC_STRONG )( t1, t2 );
    }
}

#   define MOCK_SMALL() \
        detail::is_small( actual, tolerance )
#   define MOCK_PERCENT_TOLERANCE() \
        detail::is_close( actual, expected, \
            boost::math::fpc::percent_tolerance( tolerance ) )
#   define MOCK_FRACTION_TOLERANCE() \
        detail::is_close( actual, expected, tolerance )

#endif // BOOST_VERSION < 105900

#ifdef small
#   pragma push_macro( "small" )
#   undef small
#   define MOCK_SMALL_DEFINED
#endif
    MOCK_NARY_CONSTRAINT( small, 1, ( tolerance ),
        ( MOCK_SMALL() ) )
#ifdef MOCK_SMALL_DEFINED
#   pragma pop_macro( "small" )
#endif

    MOCK_NARY_CONSTRAINT( close, 2, ( expected, tolerance ),
        ( MOCK_PERCENT_TOLERANCE() ) )

    MOCK_NARY_CONSTRAINT( close_fraction, 2, ( expected, tolerance ),
        ( MOCK_FRACTION_TOLERANCE() ) )

#undef MOCK_PERCENT_TOLERANCE
#undef MOCK_FRACTION_TOLERANCE

#ifdef near
#   pragma push_macro( "near" )
#   undef near
#   define MOCK_NEAR_DEFINED
#endif
    MOCK_NARY_CONSTRAINT( near, 2, ( expected, tolerance ),
        std::abs( actual - expected ) < tolerance )
#ifdef MOCK_NEAR_DEFINED
#   pragma pop_macro( "near" )
#endif

namespace detail
{
    template< typename Expected >
    struct equal
    {
        explicit equal( Expected expected )
            : expected_( expected )
        {}
        template< typename Actual >
        bool operator()( const Actual& actual,
            typename boost::enable_if<
                boost::has_equal_to<
                    Actual,
                    typename
                        boost::unwrap_reference< Expected >::type
                >
            >::type* = 0 ) const
        {
            return actual == boost::unwrap_ref( expected_ );
        }
        template< typename Actual >
        bool operator()( const Actual& actual,
            typename boost::disable_if<
                boost::has_equal_to<
                    Actual,
                    typename
                        boost::unwrap_reference< Expected >::type
                >
            >::type* = 0 ) const
        {
            return actual && *actual == boost::unwrap_ref( expected_ );
        }
        friend std::ostream& operator<<( std::ostream& s, const equal& e )
        {
            return s << "equal( " << mock::format( e.expected_ ) << " )";
        }
        Expected expected_;
    };

    template< typename Expected >
    struct same
    {
        explicit same( const Expected& expected )
            : expected_( detail::addressof( boost::unwrap_ref( expected ) ) )
        {}
        template< typename Actual >
        bool operator()( const Actual& actual ) const
        {
            return detail::addressof( actual ) == expected_;
        }
        friend std::ostream& operator<<( std::ostream& os, const same& s )
        {
            return os << "same( " << mock::format( *s.expected_ ) << " )";
        }
        const typename
            boost::unwrap_reference< Expected >::type* expected_;
    };

    template< typename Expected >
    struct retrieve
    {
        explicit retrieve( Expected& expected )
            : expected_( detail::addressof( boost::unwrap_ref( expected ) ) )
        {}
        template< typename Actual >
        bool operator()( const Actual& actual,
            typename boost::disable_if<
                boost::is_convertible<
                    const Actual*,
                    typename
                        boost::unwrap_reference< Expected >::type
                >
            >::type* = 0 ) const
        {
            *expected_ = actual;
            return true;
        }
        template< typename Actual >
        bool operator()( BOOST_RV_REF(Actual) actual,
            typename boost::disable_if<
                boost::is_convertible<
                    const Actual*,
                    typename
                        boost::unwrap_reference< Expected >::type
                >
            >::type* = 0 ) const
        {
            *expected_ = boost::move( actual );
            return true;
        }
        template< typename Actual >
        bool operator()( Actual& actual,
            typename boost::enable_if<
                boost::is_convertible< Actual*,
                    typename
                        boost::unwrap_reference< Expected >::type
                >
            >::type* = 0 ) const
        {
            *expected_ = detail::addressof( actual );
            return true;
        }
        friend std::ostream& operator<<( std::ostream& s, const retrieve& r )
        {
            return s << "retrieve( " << mock::format( *r.expected_ ) << " )";
        }
        typename
            boost::unwrap_reference< Expected >::type* expected_;
    };

    template< typename Expected >
    struct assign
    {
        explicit assign( const Expected& expected )
            : expected_( expected )
        {}
        template< typename Actual >
        bool operator()( Actual& actual ) const
        {
            actual = boost::unwrap_ref( expected_ );
            return true;
        }
        template< typename Actual >
        bool operator()( Actual* actual,
            typename boost::enable_if<
                boost::is_convertible<
                    typename
                        boost::unwrap_reference< Expected >::type,
                    Actual
                >
            >::type* = 0 ) const
        {
            if( ! actual )
                return false;
            *actual = boost::unwrap_ref( expected_ );
            return true;
        }
        friend std::ostream& operator<<( std::ostream& s, const assign& a )
        {
            return s << "assign( " << mock::format( a.expected_ ) << " )";
        }
        Expected expected_;
    };

    template< typename Expected >
    struct contain
    {
        explicit contain( const Expected& expected )
            : expected_( expected )
        {}
        bool operator()( const std::string& actual ) const
        {
            return actual.find( boost::unwrap_ref( expected_ ) )
                != std::string::npos;
        }
        friend std::ostream& operator<<( std::ostream& s, const contain& n )
        {
            return s << "contain( " << mock::format( n.expected_ ) << " )";
        }
        Expected expected_;
    };
}

    template< typename T >
    constraint< detail::equal< typename detail::forward_type< T >::type > > equal( BOOST_FWD_REF(T) t )
    {
        return detail::equal< typename detail::forward_type< T >::type >( boost::forward< T >( t ) );
    }

    template< typename T >
    constraint< detail::same< T > > same( T& t )
    {
        return detail::same< T >( t );
    }
    template< typename T >
    constraint< detail::retrieve< T > > retrieve( T& t )
    {
        return detail::retrieve< T >( t );
    }
    template< typename T >
    constraint< detail::assign< T > > assign( T t )
    {
        return detail::assign< T >( t );
    }
    template< typename T >
    constraint< detail::contain< T > > contain( T t )
    {
        return detail::contain< T >( t );
    }

    template< typename T >
    constraint< T > call( T t )
    {
        return constraint< T >( t );
    }
} // mock

#endif // MOCK_CONSTRAINTS_HPP_INCLUDED


// Copyright (c) Sergiu Dotenco 2010
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

/**
 * @brief WELL PRNG implementation unit test.
 * @file welltest.cpp
 */

#include <algorithm>

#define BOOST_TEST_MODULE WELL

#include <boost/range/size.hpp>
#include <boost/test/unit_test.hpp>

#include "well.hpp"

/**
 * @brief Generic WELL test case.
 *
 * The test case performs the following checks:
 * -# The last generated value is equal to the value generate by the reference
 *    implementation after @f$10^9@f$ iterations. The generator is seeded using
 *    an array filled with 1s.
 * -# The @c min and @c max methods of the @ref Well generator return 0 and
 *    @f$2^{32}-1@f$ respectively.
 *
 * @tparam RandomNumberGenerator WELL PRNG implementation type.
 * @tparam Expected The expected result after @f$10^9@f$ iterations.
 */
template
<
    class RandomNumberGenerator,
    typename RandomNumberGenerator::result_type Expected
>
class WellTestCase
{
    RandomNumberGenerator rng;

    typedef typename RandomNumberGenerator::result_type result_type;

    result_type generate()
    {
        unsigned state[RandomNumberGenerator::state_size];
        std::uninitialized_fill_n(state, boost::size(state), 1);

        unsigned* p = state;
        rng.seed(p, boost::end(state));

        result_type x;

        int iterations = 1000000000;

        while (iterations-- > 0)
            x = rng();

        return x;
    }

public:
    static void run()
    {
        WellTestCase c;

        BOOST_CHECK_EQUAL(c.generate(), Expected);
        BOOST_CHECK_EQUAL(c.rng.min(), 0U);
        BOOST_CHECK_EQUAL(c.rng.max(), ~0U);
        BOOST_CHECK_EQUAL(c.rng, c.rng);
        BOOST_CHECK(c.rng == c.rng);
    }
};

/**
 * @brief Defines the actual test case.
 *
 * @param name The name of the test case.
 * @param type WELL pseudo-random generator type.
 * @param expected The expected result after @f$10^9@f$ iterations.
 *
 * @hideinitializer
 */
#define DEFINE_TEST_CASE(name, type, expected) \
    BOOST_AUTO_TEST_CASE(name) { WellTestCase<type, expected>::run(); }

DEFINE_TEST_CASE(WELL512a, boost::random::well512a, 0x2b3fe99e)
DEFINE_TEST_CASE(WELL521a, boost::random::well521a, 0xc9878363)
DEFINE_TEST_CASE(WELL521b, boost::random::well521b, 0xb75867f6)
DEFINE_TEST_CASE(WELL607a, boost::random::well607a, 0x7b5043ea)
DEFINE_TEST_CASE(WELL607b, boost::random::well607b, 0xaedee7da)
DEFINE_TEST_CASE(WELL800a, boost::random::well800a, 0x2bfe686f)
DEFINE_TEST_CASE(WELL800b, boost::random::well800b, 0xf009e1bd)
DEFINE_TEST_CASE(WELL1024a, boost::random::well1024a, 0xd07f528c)
DEFINE_TEST_CASE(WELL1024b, boost::random::well1024b, 0x867f7993)
DEFINE_TEST_CASE(WELL19937a, boost::random::well19937a, 0xb33a2cd5)
DEFINE_TEST_CASE(WELL19937b, boost::random::well19937b, 0x191de86a)
DEFINE_TEST_CASE(WELL19937c, boost::random::well19937c, 0x243eaed5)
DEFINE_TEST_CASE(WELL21701a, boost::random::well21701a, 0x7365a269)
DEFINE_TEST_CASE(WELL23209a, boost::random::well23209a, 0x807dacb)
DEFINE_TEST_CASE(WELL23209b, boost::random::well23209b, 0xf1a77751)
DEFINE_TEST_CASE(WELL44497a, boost::random::well44497a, 0xfdd7c07b)
DEFINE_TEST_CASE(WELL44497b, boost::random::well44497b, 0x9406547b)

BOOST_AUTO_TEST_CASE(abc)
{
    typedef boost::random::well512a RandomNumberGenerator;
    RandomNumberGenerator rng;

    unsigned state[RandomNumberGenerator::state_size] = {};
    state[RandomNumberGenerator::state_size - 1] = 1;

    unsigned* p = state;
    rng.seed(p, boost::end(state));

    unsigned x;

    int iterations = 9999;

    while (iterations-- > 0)
        x = rng();

    BOOST_CHECK_EQUAL(x, 0x4df08652U);
}


//==================================================================================================
/*!

  Copyright 2015 NumScale SAS

  Distributed under the Boost Software License, Version 1.0.
  (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
*/
//==================================================================================================
#include <boost/simd/function/scalar/splatted_minimum.hpp>
#include <simd_test.hpp>
#include <boost/dispatch/meta/as_integer.hpp>
#include <boost/simd/constant/inf.hpp>
#include <boost/simd/constant/minf.hpp>
#include <boost/simd/constant/mone.hpp>
#include <boost/simd/constant/nan.hpp>
#include <boost/simd/constant/one.hpp>
#include <boost/simd/constant/zero.hpp>
#include <boost/simd/constant/pi.hpp>
#include <boost/simd/constant/three.hpp>

STF_CASE_TPL (" spatted_minimum ",  STF_NUMERIC_TYPES)
{
  namespace bs = boost::simd;

  using bs::splatted_minimum;

  // return type conformity test
   STF_EXPR_IS(splatted_minimum(T()), T);

  // specific values tests
  STF_EQUAL(splatted_minimum(bs::One<T>()), bs::One<T>());
  STF_EQUAL(splatted_minimum(bs::Zero<T>()), bs::Zero<T>());
} // end of test for signed_int_


#include <iostream>

#include <boost/numeric/ublas/matrix.hpp>
#include <boost/numeric/ublas/vector.hpp>
#include <boost/numeric/ublas/io.hpp>
#include <boost/timer.hpp>

#include <vector>
#include <math.h>

// extern "C"{
//   #include <cblas.h>
// }

#define INVALID -1

#include "NRGclasses.hpp"
#include "NRGfunctions.hpp"

void UpdateMatrices_uBLAS(CNRGbasisarray* pSingleSite,
			  CNRGbasisarray* pAeigCut,
			  CNRGbasisarray* pAbasis,
			  CNRGmatrix* NRGMats, int NumNRGMats, bool display){

  // Boost matrices
  boost::numeric::ublas::matrix<double> Zibl;
  boost::numeric::ublas::matrix<double> Zjbl;
  boost::numeric::ublas::matrix<double> fnbasis;

  boost::numeric::ublas::matrix<complex<double> > cZibl;
  boost::numeric::ublas::matrix<complex<double> > cZjbl;
  boost::numeric::ublas::matrix<complex<double> > cfnbasis;

  boost::numeric::ublas::matrix<double> fnw;
  boost::numeric::ublas::matrix<complex<double> > cfnw;

  // Check that eigenvectors are properly normalized!

  boost::numeric::ublas::matrix<complex<double> > cOneTest;

  // Check time
  boost::timer t;
  double time_elapsed;

  boost::timer t2;
  double time_lap2;

  CNRGmatrix* MatOLD = new CNRGmatrix [NumNRGMats];

  //Clear all
  for (int imats=0;imats<NumNRGMats;imats++){
    if (NRGMats[imats].NeedOld) MatOLD[imats]=NRGMats[imats];
    NRGMats[imats].ClearAll();
    NRGMats[imats].SyncNRGarray(*pAeigCut);
  }
  // end for

  // Note: assumes  Abasis and Aeig have
  // EXACTLY the same block structure... should check.

  // Loop over blocks (icount counts for each matrix)
  int icount[NumNRGMats];
  for (int ii=0;ii<NumNRGMats;ii++) icount[ii]=0;

  t2.restart();
  time_lap2=0.0;
  // Note Aeig can be a "cut" array
  for (int ibl=0;ibl<pAeigCut->NumBlocks();ibl++){
    int Nstibl=pAeigCut->GetBlockSize(ibl);

    cout << "Block i : " << ibl << "/" << pAeigCut->NumBlocks()-1
	 << " (sz=" <<  Nstibl << ") Nshell=" << pAeigCut->Nshell << endl;
    // Corresponding block size in pAbasis
    int iblBC=FindMatchBlock(pAeigCut,ibl,pAbasis);
    int NstiblBC=pAbasis->GetBlockSize(iblBC);

    for (int jbl=0;jbl<pAeigCut->NumBlocks();jbl++){
      int Nstjbl=pAeigCut->GetBlockSize(jbl);

      cout << "  Block j : " << jbl << "/" << pAeigCut->NumBlocks()-1
	   << " (sz=" <<  Nstjbl
	   << ") Time to here " << t2.elapsed()
	   << "; last lap " << t2.elapsed()-time_lap2 << endl;
      time_lap2=t2.elapsed();

      int jblBC=FindMatchBlock(pAeigCut,jbl,pAbasis);
      int NstjblBC=pAbasis->GetBlockSize(jblBC);

      //Loop matrices: check if the matrix elements are non-zero
      for (int imats=0;imats<NumNRGMats;imats++){
	if ( NRGMats[imats].CheckForMatEl(pAeigCut,ibl,jbl)   ){
	  cout << "   Updating Op imats=: " << imats << " of " << NumNRGMats-1 << endl;
	  // Set Zi,Zj
	  if (display){
	    cout << " Nshell = " << pAeigCut->Nshell << endl;
	    //cout << " Updating Op imats=: " << imats << " of " << NumNRGMats-1 << endl;
	    cout << " icount = " << icount[imats] << " to "
		 << icount[imats]+Nstibl*Nstjbl-1 << endl;
	    cout << "  Setting up Block i : " << ibl
		 << " (size " <<  Nstibl
		 << "  was bl " << iblBC << " sz " << NstiblBC
		 << ") x Block j " << jbl
		 << " (size " << Nstjbl
		 << "  was bl " << jblBC << " sz " << NstjblBC
		 << ") of " << pAeigCut->NumBlocks() << endl;
	  }
	  NRGMats[imats].MatBlockMap.push_back(ibl);
	  NRGMats[imats].MatBlockMap.push_back(jbl);
	  NRGMats[imats].MatBlockBegEnd.push_back(icount[imats]);
	  icount[imats]+=Nstibl*Nstjbl;
	  NRGMats[imats].MatBlockBegEnd.push_back(icount[imats]-1);

	  // 		  cout << "    Setting up BLAS matrices..." << endl;
	  //boost::numeric::ublas::matrix<double> Zibl(Nstibl,Nstibl);
	  //boost::numeric::ublas::matrix<double> Zjbl(Nstjbl,Nstjbl);
	  if (NRGMats[imats].IsComplex){
	    cZibl.resize(Nstibl,NstiblBC);
	    cZjbl.resize(Nstjbl,NstjblBC);
	    cZibl=pAeigCut->cEigVecCut2BLAS(ibl);
	    cZjbl=pAeigCut->cEigVecCut2BLAS(jbl);
	    cfnbasis.resize(NstiblBC,NstjblBC);
	  }else{
	    Zibl.resize(Nstibl,NstiblBC);
	    Zjbl.resize(Nstjbl,NstjblBC);
	    Zibl=pAeigCut->EigVecCut2BLAS(ibl);
	    Zjbl=pAeigCut->EigVecCut2BLAS(jbl);
	    fnbasis.resize(NstiblBC,NstjblBC);
	  }

	  if (display) cout << "    ...Zs done ..." << endl;

// 	  fnbasis.resize(NstiblBC,NstjblBC);
	  // Set-up fn basis: Loop in the basis states
	  int istbl=0;
	  for (int ist=pAbasis->GetBlockLimit(iblBC,0);
	       ist<=pAbasis->GetBlockLimit(iblBC,1);ist++){
	    //int typei=pAbasis->iType[ist];
	    //int stcfi=pAbasis->StCameFrom[ist];

	    int jstbl=0;
	    for (int jst=pAbasis->GetBlockLimit(jblBC,0);
		 jst<=pAbasis->GetBlockLimit(jblBC,1);jst++){
	      //int typej=pAbasis->iType[jst];
	      //int stcfj=pAbasis->StCameFrom[jst];

	      if (NRGMats[imats].IsComplex)
		cfnbasis(istbl,jstbl)=ZeroC;
	      else fnbasis(istbl,jstbl)=0.0;

	      //Calculate fbasis
	      if (NRGMats[imats].NeedOld){
		int iold=pAbasis->StCameFrom[ist];
		int jold=pAbasis->StCameFrom[jst];
		if (NRGMats[imats].IsComplex){
		  cfnbasis(istbl,jstbl)=(NRGMats[imats].CalcMatElCplx(pAbasis,pSingleSite,ist,jst))*MatOLD[imats].cGetMatEl(iold,jold);
		}
		  else
		fnbasis(istbl,jstbl)=(NRGMats[imats].CalcMatEl(pAbasis,pSingleSite,ist,jst))*MatOLD[imats].GetMatEl(iold,jold);
	      }
	      else{
		if (NRGMats[imats].IsComplex)
		  cfnbasis(istbl,jstbl)=NRGMats[imats].CalcMatElCplx(pAbasis,pSingleSite,ist,jst);

		else
		  fnbasis(istbl,jstbl)=NRGMats[imats].CalcMatEl(pAbasis,pSingleSite,ist,jst);
	      }
	      jstbl++;
	    }
	    // end jst loop
	    istbl++;
	  }
	  // end ist loop
	  if (display) cout << "    ...fnbasis done." << endl;

	  if (display) cout << "    Multiplying BLAS matrices (final size:"
			    << Nstibl << " x " << Nstjbl << ") ... " << endl;

	  t.restart();
	  if (NRGMats[imats].IsComplex){
	    cfnw.resize(Nstibl,Nstjbl);
// 	    noalias(cfnw)=prod (cZibl,
// 		     boost::numeric::ublas::matrix<complex<double> >(prod(cfnbasis,trans(cZjbl))) );
	    // Wrong. It should be:
	    noalias(cfnw)=prod (cZibl,
		     boost::numeric::ublas::matrix<complex<double> >(prod(cfnbasis,herm(cZjbl))) );

	  }
	  else{
	    fnw.resize(Nstibl,Nstjbl);
	    noalias(fnw)=prod (Zibl,
		     boost::numeric::ublas::matrix<double>(prod(fnbasis,trans(Zjbl))) );
	  }
	  time_elapsed=t.elapsed();
	  if (display) cout << "    ...done. Elapsed time:" << time_elapsed << endl;
	  // Debug
	  // if ( (display)&&(pAeigCut->Nshell==1)&&
	  //      ( (ibl==1)||(jbl==1) )&&
	  //      ( (imats==1) )
	  //      ) {
	  //   cout << "Z(i="<<ibl<<")  : " <<  Zibl << endl;
	  //   cout << "Z(j="<<jbl<<")  : " <<  Zjbl << endl;
	  //   cout << "fbasis   :" <<  fnbasis << endl;
	  //   cout << "Zi.fbasis.ZjT : " <<  fnw << endl;
	  //   ///
	  //   // cout << "cZ(i="<<ibl<<")  : " <<  cZibl << endl;
	  //   // cout << "cZ(j="<<jbl<<")  : " <<  cZjbl << endl;

	  //   // cOneTest.resize(NstiblBC,NstiblBC);
	  //   // noalias(cOneTest)=prod (herm(cZibl), cZibl);

	  //   // cout << "cZi+.cZi   : " <<  cOneTest << endl;

	  //   // cOneTest.resize(NstjblBC,NstjblBC);
	  //   // noalias(cOneTest)=prod (herm(cZjbl), cZjbl);

	  //   // cout << "cZj+.cZj   : " <<  cOneTest << endl;

	  //   // cout << "cfbasis   :" <<  cfnbasis << endl;
	  //   // cout << "cZi.cfbasis.cZjT : " <<  cfnw << endl;
	  //   //cout << "faux   :" <<  faux << endl;
	  //  }
	  // end debug

	  // Add to NRGMats[imats-1]
	  if (NRGMats[imats].IsComplex){
	    for (int ii=0;ii<cfnw.size1();ii++)
	      for (int jj=0;jj<cfnw.size2();jj++)
		NRGMats[imats].MatElCplx.push_back(cfnw(ii,jj));
	  }
	  else{
	    for (int ii=0;ii<fnw.size1();ii++)
	      for (int jj=0;jj<fnw.size2();jj++)
		NRGMats[imats].MatEl.push_back(fnw(ii,jj));
	  }
	  // end if is complex
	}
	//end if Q=Q'+1 etc

      }
      // end imats loop

    }
    // end jbl loop

  }
  // end ibl loop

  // Release MatOLD
  delete[] MatOLD;

  cout << " DONE updating operators in Nshell = " << pAeigCut->Nshell << endl;

}
// END subroutine

//////////////////////////////////
//////////////////////////////////
//////////////////////////////////
//////////////////////////////////

void UpdateMatrices(CNRGbasisarray* pSingleSite,CNRGbasisarray* pAeigCut,
		    CNRGbasisarray* pAbasis,
		    CNRGmatrix* NRGMats, int NumNRGMats, bool display){

  // STL vectors
  vector<double> fnbasis;
  vector<complex<double> > cfnbasis;

  vector<double> fnw;
  vector<complex<double> > cfnw;

  vector<double> auxMat1;
  vector<complex<double> > cauxMat1;

  double auxEl;
  complex<double> cauxEl;

  int ibegZibl,ibegZjbl; // Point where the blocks begin

  int ldaBC,lda,ldaaux; //=max(SizeBl,SizeBl2);

  // cblas_dgemm variables
  double ALPHA=1.0, BETA=0.0;
  //   C <- ALPHA*A.B + BETA*C

  //typedef CBLAS_ORDER CBLAS_LAYOUT; // this for backward compatibility

  // Check that eigenvectors are properly normalized!

  //boost::numeric::ublas::matrix<complex<double> > cOneTest;

  // Check time
  boost::timer t;
  double time_elapsed;

  boost::timer t2;
  double time_lap2;

  CNRGmatrix* MatOLD = new CNRGmatrix [NumNRGMats];

  //Clear all
  for (int imats=0;imats<NumNRGMats;imats++){
    if (NRGMats[imats].NeedOld) MatOLD[imats]=NRGMats[imats];
    NRGMats[imats].ClearAll();
    NRGMats[imats].SyncNRGarray(*pAeigCut);
  }
  // end for

  // Note: assumes  Abasis and Aeig have
  // EXACTLY the same block structure... should check.

  // Loop over blocks (icount counts for each matrix)
  int icount[NumNRGMats];
  for (int ii=0;ii<NumNRGMats;ii++) icount[ii]=0;

  t2.restart();
  time_lap2=0.0;
  // Note Aeig can be a "cut" array
  for (int ibl=0;ibl<pAeigCut->NumBlocks();ibl++){
    int Nstibl=pAeigCut->GetBlockSize(ibl);

    cout << "Block i : " << ibl << "/" << pAeigCut->NumBlocks()-1
	 << " (sz=" <<  Nstibl << ") Nshell=" << pAeigCut->Nshell << endl;
    // Corresponding block size in pAbasis
    int iblBC=FindMatchBlock(pAeigCut,ibl,pAbasis);
    int NstiblBC=pAbasis->GetBlockSize(iblBC);

    for (int jbl=0;jbl<pAeigCut->NumBlocks();jbl++){
      int Nstjbl=pAeigCut->GetBlockSize(jbl);

      cout << "  Block j : " << jbl << "/" << pAeigCut->NumBlocks()-1
	   << " (sz=" <<  Nstjbl
	   << ") Time to here " << t2.elapsed()
	   << "; last lap " << t2.elapsed()-time_lap2 << endl;
      time_lap2=t2.elapsed();

      int jblBC=FindMatchBlock(pAeigCut,jbl,pAbasis);
      int NstjblBC=pAbasis->GetBlockSize(jblBC);

      //Loop matrices: check if the matrix elements are non-zero
      for (int imats=0;imats<NumNRGMats;imats++){
	if ( NRGMats[imats].CheckForMatEl(pAeigCut,ibl,jbl)   ){
	  cout << "   Updating Op imats=: " << imats << " of " << NumNRGMats-1 << endl;
	  // Set Zi,Zj
	  if (display){
	    cout << " Nshell = " << pAeigCut->Nshell << endl;
	    //cout << " Updating Op imats=: " << imats << " of " << NumNRGMats-1 << endl;
	    cout << " icount = " << icount[imats] << " to "
		 << icount[imats]+Nstibl*Nstjbl-1 << endl;
	    cout << "  Setting up Block i : " << ibl
		 << " (size " <<  Nstibl
		 << "  was bl " << iblBC << " sz " << NstiblBC
		 << ") x Block j " << jbl
		 << " (size " << Nstjbl
		 << "  was bl " << jblBC << " sz " << NstjblBC
		 << ") of " << pAeigCut->NumBlocks() << endl;
	  }
	  NRGMats[imats].MatBlockMap.push_back(ibl);
	  NRGMats[imats].MatBlockMap.push_back(jbl);
	  NRGMats[imats].MatBlockBegEnd.push_back(icount[imats]);
	  icount[imats]+=Nstibl*Nstjbl;
	  NRGMats[imats].MatBlockBegEnd.push_back(icount[imats]-1);

	  ibegZibl=pAeigCut->GetBlockLimitEigv(ibl,0);
	  ibegZjbl=pAeigCut->GetBlockLimitEigv(jbl,0);

	  cfnbasis.clear();
	  fnbasis.clear();

	  if (display) cout << "    ...Zs done ..." << endl;

	  // Set-up fn basis: Loop in the basis states
	  int istbl=0;
	  for (int ist=pAbasis->GetBlockLimit(iblBC,0);
	       ist<=pAbasis->GetBlockLimit(iblBC,1);ist++){
	    //int typei=pAbasis->iType[ist];
	    //int stcfi=pAbasis->StCameFrom[ist];

	    int jstbl=0;
	    for (int jst=pAbasis->GetBlockLimit(jblBC,0);
		 jst<=pAbasis->GetBlockLimit(jblBC,1);jst++){
	      //int typej=pAbasis->iType[jst];
	      //int stcfj=pAbasis->StCameFrom[jst];

	      //Calculate fbasis
	      if (NRGMats[imats].NeedOld){
		int iold=pAbasis->StCameFrom[ist];
		int jold=pAbasis->StCameFrom[jst];
		if (NRGMats[imats].IsComplex){
		  cauxEl=(NRGMats[imats].CalcMatElCplx(pAbasis,pSingleSite,ist,jst))*MatOLD[imats].cGetMatEl(iold,jold);
		  cfnbasis.push_back(cauxEl);
		}
		else{
		  auxEl=(NRGMats[imats].CalcMatEl(pAbasis,pSingleSite,ist,jst))*MatOLD[imats].GetMatEl(iold,jold);
		  fnbasis.push_back(auxEl);
		}
	      }
	      else{
		if (NRGMats[imats].IsComplex){
		  cauxEl=NRGMats[imats].CalcMatElCplx(pAbasis,pSingleSite,ist,jst);
		  cfnbasis.push_back(cauxEl);
		}
		else{
		  auxEl=NRGMats[imats].CalcMatEl(pAbasis,pSingleSite,ist,jst);
		  fnbasis.push_back(auxEl);
		}
	      }// end if NeedOld else
	      jstbl++;
	    }
	    // end jst loop
	    istbl++;
	  }
	  // end ist loop
	  if (display) cout << "    ...fnbasis done." << endl;

	  if (display) cout << "    Multiplying BLAS matrices (final size:"
			    << Nstibl << " x " << Nstjbl << ") ... " << endl;

	  t.restart();

	  // Let's see:
	  // LDA-> No. of cols of the ORIGINAL matrix
	  //
	  //   Zibl -> (Nstibl x NstiblBC) matrix -> LDA=NstiblBC
	  //   Zjbl -> (Nstjbl x NstjblBC) matrix -> LDA=NstjblBC
	  //   fnbasis -> (NstiblBC x NstjblBC) matrix -> LDA=NstjblBC
	  //   fnw -> (Nstibl x Nstjbl) matrix    -> LDA=Nstjbl
	  // auxMat1 -> (NstiblBC x Nstjbl) matrix -> LDA=Nstjbl
	  // auxMat1 = cfnbasis_(NstiblBC x NstjblBC) . herm(cZjbl)_(NstjblBC x Nstjbl)
	  //
	  //  -> (NstiblBC x Nstjbl) matrix
	  // cfnw = cZibl_(Nstibl x NstiblBC) . auxMat1_(NstiblBC x Nstjbl)

	  if (NRGMats[imats].IsComplex){

	    cauxMat1.clear();
	    cauxMat1.resize(NstiblBC*Nstjbl,ZeroC);

	    // fnbasis_NstiblBC x NstjblBC) x [Zjbl_(Nstjbl x NstjblBC)]^+
	    // -> cMat1_(NstiblBC x Nstjbl)
	    cblas_zgemm(CblasRowMajor,  CblasNoTrans, CblasConjTrans,
			NstiblBC, Nstjbl, NstjblBC,
			&ALPHA, &cfnbasis[0], NstjblBC, &(pAeigCut->cEigVec[ibegZjbl]), NstjblBC,
			&BETA, &cauxMat1[0], Nstjbl);

	    cfnw.clear();
	    cfnw.resize(Nstibl*Nstjbl,ZeroC);

	    //  Zibl_(Nstibl x NstiblBC) x cMat1_(NstiblBC x Nstjbl)
	    //  -> fnw_(Nstibl x Nstjbl)
	    cblas_zgemm(CblasRowMajor,  CblasNoTrans, CblasNoTrans,
			Nstibl, Nstjbl, NstiblBC,
			&ALPHA, &(pAeigCut->cEigVec[ibegZibl]), NstiblBC,
			&cauxMat1[0], Nstjbl, &BETA, &cfnw[0], Nstjbl );

	  }else{

	    auxMat1.clear();
	    auxMat1.resize(NstiblBC*Nstjbl,0.0);

	    // fnbasis_NstiblBC x NstjblBC) x [Zjbl_(Nstjbl x NstjblBC)]^+
	    // -> cMat1_(NstiblBC x Nstjbl)
	    cblas_dgemm(CblasRowMajor,  CblasNoTrans, CblasConjTrans,
			NstiblBC, Nstjbl, NstjblBC,
			ALPHA, &fnbasis[0], NstjblBC, &(pAeigCut->dEigVec[ibegZjbl]), NstjblBC,
			BETA, &auxMat1[0], Nstjbl);

	    fnw.clear();
	    fnw.resize(Nstibl*Nstjbl,0.0);

	    //  Zibl_(Nstibl x NstiblBC) x auxMat1_(NstiblBC x Nstjbl)
	    //  -> fnw_(Nstibl x Nstjbl)
	    cblas_dgemm(CblasRowMajor,  CblasNoTrans, CblasNoTrans,
			Nstibl, Nstjbl, NstiblBC,
			ALPHA, &(pAeigCut->dEigVec[ibegZibl]), NstiblBC,
			&auxMat1[0], Nstjbl, BETA, &fnw[0], Nstjbl );

	  }
	  time_elapsed=t.elapsed();
	  if (display) cout << "    ...done. Elapsed time:" << time_elapsed << endl;
	  // Debug
	  // if ( (display)&&(pAeigCut->Nshell==1)&&
	  //      ( (ibl==1)||(jbl==1) )&&
	  //      ( (imats==1) )
	  //      ) {
	  //   cout << " ibl= " << ibl
	  // 	 << " jbl= " << jbl
	  // 	 << endl;
	  //   cout << " Nstibl=" << Nstibl
	  // 	 << " NstiblBC=" << NstiblBC
	  // 	 << " Nstjbl=" << Nstjbl
	  // 	 << " NstjblBC=" << NstjblBC
	  // 	 << endl;
	  //   cout << " fnbasis = ";
	  //   for (int ii=0; ii<fnbasis.size(); ii++){
	  //     if ( ii % NstjblBC ==0) cout << endl;
	  //     cout << fnbasis[ii] << " ";
	  //   }
	  //   cout<< endl;
	  //   cout << " fnw = ";
	  //   for (int ii=0; ii<fnw.size(); ii++){
	  //     if ( ii % Nstjbl ==0) cout << endl;
	  //     cout << fnw[ii] << " ";
	  //   }
	  //   cout<< endl;

	    // cout << " cfnbasis = ";
	    // for (int ii=0; ii<cfnbasis.size(); ii++){
	    //   if ( ii % NstjblBC ==0) cout << endl;
	    //   cout << cfnbasis[ii] << " ";
	    // }
	    // cout<< endl;
	    // cout << " cfnw = ";
	    // for (int ii=0; ii<cfnw.size(); ii++){
	    //   if ( ii % Nstjbl ==0) cout << endl;
	    //   cout << cfnw[ii] << " ";
	    // }
	    // cout<< endl;

	  // }
	// end debug

	  // Add to NRGMats[imats-1]
	  if (NRGMats[imats].IsComplex){
	    for (int ii=0;ii<cfnw.size();ii++)
	      NRGMats[imats].MatElCplx.push_back(cfnw[ii]);
	  }
	  else{
	    for (int ii=0;ii<fnw.size();ii++)
	      NRGMats[imats].MatEl.push_back(fnw[ii]);
	  }
	  // end if is complex
	}
	//end if Q=Q'+1 etc

      }
      // end imats loop

    }
    // end jbl loop
  }
  // end ibl loop

  // Release MatOLD
  delete[] MatOLD;

  cout << " DONE updating operators in Nshell = " << pAeigCut->Nshell << endl;

}
// END subroutine

//////////////////////////////////
//////////////////////////////////
//////////////////////////////////


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

  @copyright 2016 NumScale SAS

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

#if defined(DOXYGEN_ONLY)
namespace boost { namespace simd
{
  /*!
    @ingroup group-swar

    Build a value of type @c T from a list of scalar values of types @c Args

    Splatted product

    Computes the splatted product of the of its argument.

    @par Semantic

    For any type @c T and value @c x of type @c T:

    @code
    T r = splatted_prod(x);
    @endcode

    is similar to:

    @code
    T product = 1;
    for(int i=0;i<T::static_size;++i) product *= x[i];
    for(int i=0;i<T::static_size;++i) r[i] = product;
    @endcode

  **/
  template<typename T, typename.. Args> auto make(Args&&... args) {}
} }
#endif

#include <boost/simd/function/scalar/make.hpp>
#include <boost/simd/function/simd/make.hpp>

#endif


///////////////////////////////////////////////////////////////////////////////
// statistics::model::wrap::aggregate::prior_model_dataset.hpp               //
//                                                                           //
//  Copyright 2009 Erwann Rogard. Distributed under the Boost                //
//  Software License, Version 1.0. (See accompanying file                    //
//  LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)         //
///////////////////////////////////////////////////////////////////////////////
#ifndef BOOST_STATISTICS_MODEL_WRAP_AGGREGATE_PRIOR_MODEL_DATASET_HPP_ER_2009
#define BOOST_STATISTICS_MODEL_WRAP_AGGREGATE_PRIOR_MODEL_DATASET_HPP_ER_2009
#include <boost/statistics/model/wrap/unary/prior.hpp>
#include <boost/statistics/model/wrap/aggregate/model_dataset.hpp>

namespace boost{
namespace statistics{
namespace model{

    template<typename D,typename M,typename Rx,typename Ry>
    class prior_model_dataset_ :
        public prior_wrapper<D>,
        public model_dataset_<M,Rx,Ry>
    {
        typedef prior_wrapper<D> prior_;
        typedef model_dataset_<M,Rx,Ry> model_dataset_;
    public:
        typedef prior_model_dataset_<D,M,Rx,Ry> prior_model_dataset_type;

        // Construction
        prior_model_dataset_();
        prior_model_dataset_(prior_ prior,model_dataset_ model_dataset);
        prior_model_dataset_(
            prior_ prior,
            typename model_dataset_::model_wrapper_type m,
            typename model_dataset_::covariates_wrapper_type covs,
            typename model_dataset_::responses_wrapper_type resps
        );
        prior_model_dataset_(const prior_model_dataset_&);

    };

    // Implementation //

    template<typename D,typename M,typename Rx,typename Ry>
    prior_model_dataset_<D,M,Rx,Ry>::prior_model_dataset_()
        : prior_(),model_dataset_(){}

    template<typename D,typename M,typename Rx,typename Ry>
    prior_model_dataset_<D,M,Rx,Ry>::prior_model_dataset_(
            prior_ prior,
            typename model_dataset_::model_wrapper_type m,
            typename model_dataset_::covariates_wrapper_type covs,
            typename model_dataset_::responses_wrapper_type resps
    ) : prior_( prior ), model_dataset_ ( m, covs, resps) {}

    template<typename D,typename M,typename Rx,typename Ry>
    prior_model_dataset_<D,M,Rx,Ry>::prior_model_dataset_(
        const prior_model_dataset_& that)
    :prior_( that ),model_dataset_( that ) {}

    // Free functions

    template<typename D,typename M,typename Rx,typename Ry>
    prior_model_dataset_<D,M,Rx,Ry>
    make_prior_model_dataset(
        const D& d,
        const M& m,
        const Rx& rx,
        const Ry& ry
    ){
        typedef prior_model_dataset_<D,M,Rx,Ry> result_type;
        return result_type(d,m,rx,ry);
    }

}// model
}// statistics
}// boost

#endif