#pragma once

#include "template/domain_independent/strategy.hpp"
#include "template/algorithm/iteration/iteration_util.hpp"
#include "template/hand_work/abstr_hits.hpp"
// #include "util/combinatorics.h"
#include <boost/log/trivial.hpp>
#include <forward_list>
#include <functional>

template<typename>
class MultithreadBestResponse;

template<typename Poker>
class MultithreadBestResponse{

public:
    using Poker_t = Poker;
    explicit MultithreadBestResponse(unsigned int);
    ~MultithreadBestResponse();

    double compute_br(const std::function<double(int, uint64_t)> get_reach_funcs[Poker_t::num_players], int player, const PrAbstrHits<Poker>& abstrhits, Strategy<Poker>* strat = nullptr);

protected:
    void clear_();
    void init_(const PrAbstrHits<Poker>& abstrhits);
    void malloc_round_(unsigned int round, const PrAbstrHits<Poker>& abstrhits);
    void free_round_(unsigned int round);
    void push_terminal_(unsigned int round, const PrAbstrHits<Poker>& abstrhits);
    void compress_chance_(unsigned int round, const PrAbstrHits<Poker>& abstrhits);
    void push_chance_(unsigned int round, const PrAbstrHits<Poker>& abstrhits);
    void handle_terminal_(const std::function<double(int, uint64_t)> get_reach_funcs[Poker_t::num_players], int player, type::card_t holes[Poker_t::num_players][Poker_t::hole_len[Poker_t::num_rounds-1]], type::card_t board[Poker_t::board_len[Poker_t::num_rounds-1]], int round, const PrAbstrHits<Poker>& abstrhits);

protected:

    struct SingleBestResponse;
    friend struct SingleBestResponse;

    const unsigned int thread_num;
    SingleBestResponse **processors;
    std::shared_ptr<tlx::ThreadPool> sp_pool;

    typedef std::atomic<double> async_award_t;
    async_award_t root_award;
    async_award_t* seq_prbucket_awards[Game<Poker_t>::num_total];//[sequence][prbucket]
    std::vector<int> terminals_round[Poker_t::num_rounds];
    std::vector<int> chances_round[Poker_t::num_rounds];

    struct SingleBestResponse{
    public:
        MultithreadBestResponse& host;
        unsigned int thread_idx;
        unsigned int thread_num;

        int64_t prbucket_begin_round[Poker_t::num_rounds];
        int64_t prbucket_step_round[Poker_t::num_rounds];
        std::vector<uint64_t> addbucket_vec[Poker_t::num_rounds];

        typedef double award_t;
        award_t* seq_prbucket_awards[Game<Poker_t>::num_total]{nullptr};
        award_t* seq_addbucket_awards[Game<Poker_t>::num_total]{nullptr};
        int *br_strategy_equivalent[Game<Poker_t>::num_internal]{nullptr};//[internal_sequence][prbucket] // 等价最佳价值策略的数量，对这些策略使用均匀分布，其他策略都是0
        int unhandled_successor[Game<Poker_t>::num_total];//[Game<Poker_t>::num_total]
        std::forward_list<int> unhandled_seqs_round[Poker_t::num_rounds];
        std::vector<int> tohandle_seqs_round[Poker_t::num_rounds];

    public:
        explicit SingleBestResponse(unsigned int thread_idx, unsigned int thread_num, MultithreadBestResponse* host): thread_idx(thread_idx), thread_num(thread_num), host(*host){

            std::memset(seq_prbucket_awards, 0, sizeof(award_t*[Game<Poker_t>::num_total]));
            std::memset(br_strategy_equivalent, 0, sizeof(int*[Game<Poker_t>::num_internal]));
        }

        void clear(){

            for(int i = 0; i < Game<Poker_t>::num_total; ++i){
                if(seq_prbucket_awards[i])
                    delete[] seq_prbucket_awards[i];
            }
            std::memset(seq_prbucket_awards, 0, sizeof(award_t*[Game<Poker_t>::num_total]));

            for(int i = 0; i < Game<Poker_t>::num_internal; ++i){
                if(br_strategy_equivalent[i])
                    delete[] br_strategy_equivalent[i];
            }
            std::memset(br_strategy_equivalent, 0, sizeof(int*[Game<Poker_t>::num_internal]));

            for(int i = 0; i < Poker_t::num_rounds; ++i){
                unhandled_seqs_round[i].clear();
                tohandle_seqs_round[i].clear();
            }

            for(int r = 0; r<Poker_t::num_rounds; ++r){
                prbucket_begin_round[r] = -1;
                prbucket_step_round[r] = -1;
            }
        }

        void init(const int* unhandled_successor_, const std::forward_list<int>* unhandled_seqs_round_, const std::vector<int>* tohandle_seqs_round_, const PrAbstrHits<Poker>& abstrhits){
            std::copy(unhandled_successor_, unhandled_successor_ + Game<Poker_t>::num_total, unhandled_successor);
            for (int r = 0; r < Poker_t::num_rounds; ++r){
                unhandled_seqs_round[r] = unhandled_seqs_round_[r];
                tohandle_seqs_round[r] = tohandle_seqs_round_[r];
            }

            for(int r = 0; r<Poker_t::num_rounds; ++r){
                addbucket_vec[r].clear();

                if (abstrhits.round_addbucket_size(r) > 0) {
                    addbucket_vec[r] = abstrhits.addbucket_partitionvec_by_threads(thread_idx, r);
                    prbucket_begin_round[r] = addbucket_vec[r].empty()? abstrhits.round_prbucket_size(r): abstrhits.addbucket_decompose_to_prbuckets(addbucket_vec[r].front(), r).front();
                    prbucket_step_round[r] = addbucket_vec[r].empty()? 0: abstrhits.addbucket_decompose_to_prbuckets(addbucket_vec[r].back(), r).back() - prbucket_begin_round[r] + 1;
                }
                else {
                    const uint64_t round_prbucket_size = abstrhits.round_prbucket_size(r);
                    const uint64_t step = (round_prbucket_size + thread_num - 1) / thread_num;
                    int64_t real_step_begin = thread_idx * step;
                    int64_t real_step_end = (real_step_begin + step > round_prbucket_size) ? round_prbucket_size : real_step_begin + step;
                    prbucket_begin_round[r] = real_step_begin;
                    prbucket_step_round[r] = real_step_end > real_step_begin? real_step_end - real_step_begin : 0;
                }
            }
        }

        void iterate_round(int round, int player, const PrAbstrHits<Poker>& abstrhits, Strategy<Poker>* strat = nullptr){

            if (strat){
                // do {
                //     iterate_round_seqs_(round, player, strat);
                // } while(update_and_still_in_round_(round));
            }
            else {
                do {
                    iterate_round_seqs_(round, player, abstrhits);
                } while(update_and_still_in_round_(round));
            }
        }
    private:
//         void iterate_round_seqs_(int round, int player, Strategy<Poker>* strat){
//             uint64_t abstrbucket_step = abstrbucket_step_round[round];
//             uint64_t abstrbucket_begin = abstrbucket_begin_round[round];

//             for(const int u : tohandle_seqs_round[round]) {
//                 if (u < Game<Poker_t>::num_chance && Game<Poker_t>::round[u] == round)
//                     continue;

//                 int parent = Game<Poker_t>::parent[u];

//                 if (player != Game<Poker_t>::whose_turn[parent]){

//                     if(!seq_prbucket_awards[parent]){
//                         seq_prbucket_awards[parent] = new award_t[abstrbucket_step];
//                         std::memset(seq_prbucket_awards[parent], 0, sizeof(award_t) * abstrbucket_step);
//                     }

//                     for(uint64_t i = 0; i<abstrbucket_step; ++i){
//                         seq_prbucket_awards[parent][i] += seq_prbucket_awards[u][i];
//                     }
//                 }
//                 else {
//                     // 临时使用的策略组
//                     int action_size = Game<Poker_t>::num_actions[parent];
//                     double *temp_tuple = new double[action_size];

//                     if(!seq_prbucket_awards[parent]) {
//                         seq_prbucket_awards[parent] = new award_t[abstrbucket_step];
//                         std::fill(seq_prbucket_awards[parent], seq_prbucket_awards[parent] + abstrbucket_step, std::numeric_limits<award_t>::lowest());

//                         assert(!br_strategy_equivalent[parent]);
//                         br_strategy_equivalent[parent] = new int[abstrbucket_step];
//                         std::memset(br_strategy_equivalent[parent], 0, sizeof(int) * abstrbucket_step);
//                     }

//                     for(uint64_t i = 0; i<abstrbucket_step; ++i){
//                         if(seq_prbucket_awards[parent][i] > seq_prbucket_awards[u][i])
//                             // 目标玩家的收益较小不予考虑
//                             continue;

//                         // 遇到更大的目标玩家收益更新计数
//                         if(seq_prbucket_awards[parent][i] < seq_prbucket_awards[u][i]){
//                             br_strategy_equivalent[parent][i] = 0;
//                         }

//                         // 获得之前的策略
//                         const double* original_tuple = strat->get_strategy(Sequence<Poker_t>(parent), i + isomorphism_begin); // 这个策略是归一化的，和一定是1.0
//                         assert(original_tuple);
//                         std::memcpy(temp_tuple, original_tuple, sizeof(double)*action_size);

//                         // 对策略进行逆归一化，每个目标位置应该为1.
//                         for(int j = 0; j<action_size; ++j)
//                             temp_tuple[j] *= br_strategy_equivalent[parent][i];
//                         // 新的同策略位置也加1
//                         int action_idx = Game<Poker_t>::action_result_from_idx[u];
//                         temp_tuple[action_idx] += 1.;
//                         strat->set_strategy(Sequence<Poker_t>(parent), i + isomorphism_begin, temp_tuple); // 这里会自动归一化

//                         ++br_strategy_equivalent[parent][i];

//                         seq_prbucket_awards[parent][i] = ( seq_prbucket_awards[parent][i] * (br_strategy_equivalent[parent][i] - 1)
//                                                     + seq_prbucket_awards[u][i] )
//                                                     / br_strategy_equivalent[parent][i];
//                     }
//                     delete[] temp_tuple;
//                 }
// //////////////////////////////////////////////////////////////////////////////////////////////////
//                 if (player == Game<Poker_t>::whose_turn[u] && u < Game<Poker_t>::num_internal) {
//                     delete[] br_strategy_equivalent[u];
//                     br_strategy_equivalent[u] = nullptr;
//                 }
//                 delete[] seq_prbucket_awards[u];
//                 seq_prbucket_awards[u] = nullptr;
//             }
//         }

        void iterate_round_seqs_(int round, int player, const PrAbstrHits<Poker>& abstrhits){
            uint64_t prbucket_step = prbucket_step_round[round];
            uint64_t prbucket_begin = prbucket_begin_round[round];
            std::size_t addbucket_vec_size = addbucket_vec[round].size();

            for(const int u : tohandle_seqs_round[round]) {
                if (u < Game<Poker_t>::num_chance && Game<Poker_t>::round[u] == round)
                    continue;

                int parent = Game<Poker_t>::parent[u];

                if (player != Game<Poker_t>::whose_turn[parent]){

                    if(!seq_prbucket_awards[parent]){
                        seq_prbucket_awards[parent] = new award_t[prbucket_step];
                        std::memset(seq_prbucket_awards[parent], 0, sizeof(award_t) * prbucket_step);
                        if(addbucket_vec_size > 0) {
                            seq_addbucket_awards[parent] = new award_t[addbucket_vec_size];
                            std::memset(seq_addbucket_awards[parent], 0, sizeof(award_t) * addbucket_vec_size);
                        }
                    }

                    for(uint64_t i = 0; i < prbucket_step; ++i){
                        seq_prbucket_awards[parent][i] += seq_prbucket_awards[u][i];
                    }

                    if(addbucket_vec_size > 0) {
                        for (uint64_t addbucket_idx = 0; addbucket_idx < addbucket_vec_size; ++addbucket_idx) {
                            seq_addbucket_awards[parent][addbucket_idx] += seq_addbucket_awards[u][addbucket_idx];
                        }
                    }

                }
                else {
                    if(!seq_prbucket_awards[parent]) {
                        seq_prbucket_awards[parent] = new award_t[prbucket_step];
                        std::fill(seq_prbucket_awards[parent], seq_prbucket_awards[parent] + prbucket_step, std::numeric_limits<award_t>::lowest());
                        if (addbucket_vec_size > 0) {
                            seq_addbucket_awards[parent] = new award_t[addbucket_vec_size];
                            std::fill(seq_addbucket_awards[parent], seq_addbucket_awards[parent] + addbucket_vec_size, std::numeric_limits<award_t>::lowest());
                        }
                    }

                    if(addbucket_vec_size > 0) {
                        for (uint64_t addbucket_idx = 0; addbucket_idx < addbucket_vec_size; ++addbucket_idx) {
                            uint64_t addbucket = addbucket_vec[round][addbucket_idx];
                            if(seq_addbucket_awards[parent][addbucket_idx] < seq_addbucket_awards[u][addbucket_idx]) {
                                seq_addbucket_awards[parent][addbucket_idx] = seq_addbucket_awards[u][addbucket_idx];
                                for (const uint64_t prbucket : abstrhits.addbucket_decompose_to_prbuckets(addbucket, round)){
                                    seq_prbucket_awards[parent][prbucket-prbucket_begin] = seq_prbucket_awards[u][prbucket-prbucket_begin];
                                }
                            }
                        }
                    }
                    else {
                        for(uint64_t i = 0; i < prbucket_step; ++i){
                            if(seq_prbucket_awards[parent][i] < seq_prbucket_awards[u][i])
                                seq_prbucket_awards[parent][i] = seq_prbucket_awards[u][i];
                        }
                    }
                }
//////////////////////////////////////////////////////////////////////////////////////////////////
                delete[] seq_prbucket_awards[u];
                seq_prbucket_awards[u] = nullptr;
                if (addbucket_vec_size > 0) {
                    delete[] seq_addbucket_awards[u];
                    seq_addbucket_awards[u] = nullptr;
                }
            }
        }

        bool update_and_still_in_round_(int round){

            // 层迭代之后，迭代节点的后继就减少了
            for(const int u : tohandle_seqs_round[round]) {
                if (u < Game<Poker_t>::num_chance && Game<Poker_t>::round[u] == round)
                    continue; // 不能减少本轮次前端chance

                int parent = Game<Poker_t>::parent[u];
                --unhandled_successor[parent];
                assert(unhandled_successor[parent]>=0);
            }

            // 本轮次后继节点为0的点进入下次迭代
            tohandle_seqs_round[round].clear();
            for(auto bt = unhandled_seqs_round[round].before_begin(), it = std::next(bt), et = unhandled_seqs_round[round].end(); it!=et; it = std::next(bt)) {
                int u = *it;
                if(unhandled_successor[u] == 0){
                    tohandle_seqs_round[round].push_back(u);
                    unhandled_seqs_round[round].erase_after(bt); 
                }
                else{
                    ++bt;
                }
            }

            // 本轮次该处理的seq没了就结束iterate
            return !tohandle_seqs_round[round].empty();
        }

    };

};

template<typename Poker>
MultithreadBestResponse<Poker>::MultithreadBestResponse(unsigned int thread_num): thread_num(thread_num){
    sp_pool = std::make_shared<tlx::ThreadPool>(thread_num);

    std::memset(seq_prbucket_awards, 0, sizeof(async_award_t*[Game<Poker_t>::num_total]));

    processors = new SingleBestResponse*[thread_num];
    for(unsigned th = 0; th < thread_num; ++th){
        processors[th] = new SingleBestResponse(th, thread_num, this);
    }

    for(int i = 0; i<Game<Poker_t>::num_chance; ++i){
        int round = Game<Poker_t>::round[i];
        chances_round[round].push_back(i);
    }

    for(int i = Game<Poker_t>::num_internal; i<Game<Poker_t>::num_total; ++i){
        int round = Game<Poker_t>::round[i];
        terminals_round[round].push_back(i);
    }
}

template<typename Poker>
MultithreadBestResponse<Poker>::~MultithreadBestResponse(){
    clear_();

    for(unsigned th = 0; th < thread_num; ++th){
        delete processors[th];
    }
    delete[] processors;
}

template<typename Poker>
void MultithreadBestResponse<Poker>::clear_(){

    for(int i = 0; i < Game<Poker_t>::num_total; ++i){
        if(seq_prbucket_awards[i])
            delete[] seq_prbucket_awards[i];
    }
    std::memset(seq_prbucket_awards, 0, sizeof(async_award_t*[Game<Poker_t>::num_total]));

    for(unsigned th = 0; th < thread_num; ++th){
        processors[th]->clear();
    }
}

template<typename Poker>
void MultithreadBestResponse<Poker>::init_(const PrAbstrHits<Poker>& abstrhits){
    int unhandled_successor[Game<Poker_t>::num_total];
    //只对前num_internal初始化动作数，后terminal个都是0
    std::copy(Game<Poker_t>::num_actions, Game<Poker_t>::num_actions + Game<Poker_t>::num_internal, unhandled_successor);
    std::fill(unhandled_successor + Game<Poker_t>::num_internal, unhandled_successor + Game<Poker_t>::num_total, 0);

    std::forward_list<int> unhandled_seqs_round[Poker_t::num_rounds];
     //链表天然从后向前，每轮次都是先非chance内部节点到chance节点
    for(int i = 0; i<Game<Poker_t>::num_chance; ++i){
        int round = Game<Poker_t>::round[i];
        unhandled_seqs_round[round].push_front(i);
    }
    for(int i = Game<Poker_t>::num_chance; i<Game<Poker_t>::num_internal; ++i){
        int round = Game<Poker_t>::round[i];
        unhandled_seqs_round[round].push_front(i);
    }

    std::vector<int> tohandle_seqs_round[Poker_t::num_rounds];
    for (int r = 0; r < Poker_t::num_rounds; ++r) {
        tohandle_seqs_round[r] = terminals_round[r];
        if(r < Poker_t::num_rounds-1) { // 对于非最后一街要把下一轮的chance算入当前轮次没解决的seq
            tohandle_seqs_round[r].insert(tohandle_seqs_round[r].end(), chances_round[r+1].begin(), chances_round[r+1].end());
        }
    }

    for(unsigned int th = 0; th < thread_num; ++th){
        sp_pool->enqueue([this, th, &unhandled_successor, &unhandled_seqs_round, &tohandle_seqs_round, &abstrhits](){
            processors[th]->init(unhandled_successor, unhandled_seqs_round, tohandle_seqs_round, abstrhits);
        });
    }

    sp_pool->loop_until_empty();
}


template<typename Poker>
double MultithreadBestResponse<Poker>::compute_br(const std::function<double(int, uint64_t)> get_reach_funcs[Poker_t::num_players], int player, const PrAbstrHits<Poker>& abstrhits, Strategy<Poker>* strat) {
    clear_();
    init_(abstrhits);
    if(strat)
        strat->register_player(player);
    
    double ret=0;

    for (int round = Poker_t::num_rounds-1; round >= 0; --round) {

        // 分配当前轮次的后端（terminal）与前端（chance）的空间，其中terminal的长度是本轮次prbucket数，chance的长度是上一轮次的prbucket数，round==0时chace的长度为1
        // |----....****| 前,后/ 主线程,分线程/ last iso, iso
        // BOOST_LOG_TRIVIAL(debug) << "malloc_round: "<< round;
        malloc_round_(round, abstrhits); // host

        //  |----....****|
        //  |        ____|
        // BOOST_LOG_TRIVIAL(debug) << "deal_terminal: "<< round;
        iteration::multithread_dealing_upto_current_round_hands<Poker_t>(sp_pool, std::bind(&MultithreadBestResponse<Poker_t>::handle_terminal_, this, get_reach_funcs, player, std::placeholders::_1, std::placeholders::_2, round, std::cref(abstrhits)), round);

        // atomic数据到trivial数据的映射，仅该轮后端terminal
        //  |........****|  --> ||........****||
        // BOOST_LOG_TRIVIAL(debug) << "push_terminal: "<< round;
        push_terminal_(round, abstrhits);

        //                    |........****|**** ==> |****........|....
        // BOOST_LOG_TRIVIAL(debug) << "iterate_round: "<< round;
        for(unsigned int th = 0; th < thread_num; ++th){
            sp_pool->enqueue([this, th, round, player, &abstrhits, strat](){
                processors[th]->iterate_round(round, player, abstrhits, strat);
            });
        }

        sp_pool->loop_until_empty();

        // |----........|.... <-- |****........|....
        // BOOST_LOG_TRIVIAL(debug) << "compress_chance: "<< round;
        compress_chance_(round, abstrhits);

        // atomic数据到trivial数据的映射，仅该轮前端chance
        // |----........|.... --> |----........|....
        if (round > 0) {
            // BOOST_LOG_TRIVIAL(debug) << "push_chance: "<< round;
            push_chance_(round, abstrhits);
        }
        else {
            // std::copy(seqs_awards[0][0], seqs_awards[0][0] + Poker_t::num_players, evs);
            ret = seq_prbucket_awards[0][0];
        }

        // |----....****|
        // BOOST_LOG_TRIVIAL(debug) << "free_round: "<< round;
        free_round_(round);
    }

    return ret;
}

template<typename Poker>
void MultithreadBestResponse<Poker>::malloc_round_(unsigned int round, const PrAbstrHits<Poker>& abstrhits){

    uint64_t prbucket_size = abstrhits.round_prbucket_size(round);
    for (unsigned int u : terminals_round[round]){
        seq_prbucket_awards[u] = new async_award_t[prbucket_size]{0};
    }

    uint64_t last_prbucket_size = round == 0? 1 : abstrhits.round_prbucket_size(round-1);
    for (unsigned int u : chances_round[round]){
        seq_prbucket_awards[u] = new async_award_t[last_prbucket_size]{0};
    }
}

template<typename Poker>
void MultithreadBestResponse<Poker>::handle_terminal_( const std::function<double(int, uint64_t)> get_reach_funcs[Poker_t::num_players]
                                                     , int player
                                                     , type::card_t holes[Poker_t::num_players][Poker_t::hole_len[Poker_t::num_rounds-1]], type::card_t board[Poker_t::board_len[Poker_t::num_rounds-1]]
                                                     , int round
                                                     , const PrAbstrHits<Poker>& abstrhits) {
    Hand<Poker_t> temp_hand(holes[0], board, round);
    uint64_t player_prbucket_or_opponent_priso[Poker_t::num_players];
    // bool handle_addbucket = abstrhits.round_addbucket_size(r) > 0;
    // uint64_t player_addbucket;
    
    for(int i = 0; true; ) {
        uint64_t priso = temp_hand.get_hand_isomorphism(0);
        if (i == player) {
            player_prbucket_or_opponent_priso[i] = abstrhits.abstract_view(priso, round);
        }
        else {
            player_prbucket_or_opponent_priso[i] = priso;
        }
        if (++i >= Poker_t::num_players)
            break;
        temp_hand.change_hole(holes[i]);
    }

//////////////////////////////////////////////////////////////////
    int ranks[Poker_t::num_players];
    Evaluator<Poker_t>::evaluate_ranks(holes, board, ranks);

    double awards[Poker_t::num_players];
    for(const int u : terminals_round[round]){
        Sequence<Poker_t> seq(u);
        assert(seq.is_terminal());
        assert(Game<Poker_t>::round[u] == round);
        if(seq.is_fold()){
            seq.deal_fold(awards);
        }
        else {
            seq.deal_showdown(ranks, awards);
        }

        double pr = 1.;
        for (int i = 0; i < Poker_t::num_players; ++i){
            if(player == i)
                continue;
            pr *= get_reach_funcs[i](u, player_prbucket_or_opponent_priso[i]);
        }

        double accumulate_value = pr * awards[player] * (1.0/ Poker_t::opponents_holes_after_single_deal_combine_num_round[round]);
        seq_prbucket_awards[u][player_prbucket_or_opponent_priso[player]] += accumulate_value;
    }
}

template<typename Poker>
void MultithreadBestResponse<Poker>::push_terminal_(unsigned int round, const PrAbstrHits<Poker>& abstrhits){

    for (unsigned th = 0; th < thread_num; ++th){
        
        sp_pool->enqueue([this, th, round, &abstrhits](){
            uint64_t prbucket_step = processors[th]->prbucket_step_round[round];
            uint64_t prbucket_begin = processors[th]->prbucket_begin_round[round];
            std::size_t addbucket_size = processors[th]->addbucket_vec[round].size();

            // 先处理terminal，这些都在tohandle里面
            for(int u : terminals_round[round]){
                processors[th]->seq_prbucket_awards[u] = new SingleBestResponse::award_t[prbucket_step];
                for(uint64_t i = 0; i<prbucket_step; ++i) {
                    processors[th]->seq_prbucket_awards[u][i] = seq_prbucket_awards[u][i+prbucket_begin];
                }

                if (addbucket_size > 0) {
                    processors[th]->seq_addbucket_awards[u] = new SingleBestResponse::award_t[addbucket_size];
                    std::fill(processors[th]->seq_addbucket_awards[u], processors[th]->seq_addbucket_awards[u]+addbucket_size, 0);
                    for (uint64_t addbucket_idx = 0; addbucket_idx < addbucket_size; ++addbucket_idx) {
                        uint64_t addbucket = processors[th]->addbucket_vec[round][addbucket_idx];
                        for (const uint64_t prbucket : abstrhits.addbucket_decompose_to_prbuckets(addbucket, round)){
                            processors[th]->seq_addbucket_awards[u][addbucket_idx] += seq_prbucket_awards[u][prbucket];
                        }
                    }
                }
            }
        });
    }
    sp_pool->loop_until_empty();
}

template<typename Poker>
void MultithreadBestResponse<Poker>::compress_chance_(unsigned int round, const PrAbstrHits<Poker>& abstrhits){

    for(unsigned int th = 0; th < thread_num; ++th) {
        sp_pool->enqueue([this, round, th, &abstrhits](){

            for ( uint64_t i = 0, prbucket_begin =  processors[th]->prbucket_begin_round[round], prbucket_step = processors[th]->prbucket_step_round[round]
                ; i < prbucket_step; ++i){
                //计算上一层的idx
                uint64_t prbucket = prbucket_begin + i;
                // int64_t current_prbucket_hit = abstrhits.current_prbucket_hits(prbucket, round);
                uint64_t last_prbucket = abstrhits.prbucket_current_to_last(prbucket, round);
                int64_t last_prbucket_hit = abstrhits.last_prbucket_hits(last_prbucket, round);

                double hit_rate = 1.0 /* 1.0 / (current_prbucket_hit) */ // 1/{h(r)}
                                * 1.0/last_prbucket_hit /* *(current_prbucket_hit)/ */ // P{h(r)|h(r-1)}
                                * (round == 0? 1: abstrhits.current_prbucket_hits(last_prbucket, round-1)) //{h(r-1)}
                                ;
                for(int u : chances_round[round]) {
                    seq_prbucket_awards[u][last_prbucket] += processors[th]->seq_prbucket_awards[u][i] * hit_rate;
                }
            }

            //释放下一层空间
            for(int u : chances_round[round]) {
                delete[] processors[th]->seq_prbucket_awards[u];
                processors[th]->seq_prbucket_awards[u] = nullptr;
            }

            if (processors[th]->addbucket_vec[round].size()>0) {
                for(int u : chances_round[round]) {
                    delete[] processors[th]->seq_addbucket_awards[u];
                    processors[th]->seq_addbucket_awards[u] = nullptr;
                }
            }
        });

    }
    sp_pool->loop_until_empty();
}

template<typename Poker>
void MultithreadBestResponse<Poker>::push_chance_(unsigned int round, const PrAbstrHits<Poker>& abstrhits){
    assert(round > 0);

    for (unsigned th = 0; th < thread_num; ++th){
        
        sp_pool->enqueue([this, th, round, &abstrhits](){
            int last_round = round - 1;
            uint64_t last_prbucket_step = processors[th]->prbucket_step_round[last_round];
            uint64_t last_prbucket_begin = processors[th]->prbucket_begin_round[last_round];
            std::size_t last_addbucket_size = processors[th]->addbucket_vec[last_round].size();

            // 再处理extract from chance
            for(int u : chances_round[round]){
                processors[th]->seq_prbucket_awards[u] = new SingleBestResponse::award_t[last_prbucket_step];
                for(uint64_t i = 0; i<last_prbucket_step; ++i) {
                    processors[th]->seq_prbucket_awards[u][i] = seq_prbucket_awards[u][i+last_prbucket_begin];
                }

                if (last_addbucket_size > 0) {
                    processors[th]->seq_addbucket_awards[u] = new SingleBestResponse::award_t[last_addbucket_size];
                    std::fill(processors[th]->seq_addbucket_awards[u], processors[th]->seq_addbucket_awards[u]+last_addbucket_size, 0);
                    for (uint64_t last_addbucket_idx = 0; last_addbucket_idx < last_addbucket_size; ++last_addbucket_idx) {
                        uint64_t last_addbucket = processors[th]->addbucket_vec[last_round][last_addbucket_idx];
                        for (const uint64_t last_prbucket : abstrhits.addbucket_decompose_to_prbuckets(last_addbucket, last_round)){
                            processors[th]->seq_addbucket_awards[u][last_addbucket_idx] += seq_prbucket_awards[u][last_prbucket];
                        }
                    }
                }
            }
        });
    }
    sp_pool->loop_until_empty();
}

template<typename Poker>
void MultithreadBestResponse<Poker>::free_round_(unsigned int round){

    for (unsigned int i : terminals_round[round]){
        delete[] seq_prbucket_awards[i];
        seq_prbucket_awards[i] = nullptr;
    }


    for (unsigned int i : chances_round[round]){
        delete[] seq_prbucket_awards[i];
        seq_prbucket_awards[i] = nullptr;
    }
}

