#include "naiveexpectedvaluecalculator.hpp"

#undef NDEBUG
#include <algorithm>
#include <assert.h>
#include <fstream>
#include <numeric>

#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/dll.hpp>

#include "mahjong/requiredtileselector.hpp"
#include "mahjong/syanten.hpp"
#include "mahjong/unnecessarytileselector.hpp"
#include "mahjong/utils.hpp"

namespace mahjong
{

NaiveExpectedValueCalculator::NaiveExpectedValueCalculator()
    : calc_syanten_down_(false)
    , calc_tegawari_(false)
    , calc_double_reach_(false)
    , calc_ippatu_(false)
    , calc_haitei_(false)
    , calc_uradora_(false)
    , calc_akatile_tumo_(false)
    , maximize_win_prob_(false)
    , discard_cache_(5) // 0(聴牌) ~ 4(4向聴)
    , draw_cache_(5)    // 0(聴牌) ~ 4(4向聴)
{
    make_uradora_table();
}

/**
 * @brief 期待値を計算する。
 *
 * @param hand 手牌
 * @param score_calculator 点数計算機
 * @param dora_indicators ドラ表示牌の一覧
 * @param syanten_type 向聴数の種類
 * @param flag フラグ
 * @return 各打牌の情報
 */
std::tuple<bool, std::vector<Candidate>>
NaiveExpectedValueCalculator::calc(const Hand &hand, const ScoreCalculator &score_calculator,
                                   const std::vector<int> &dora_indicators, int syanten_type,
                                   int turn, int flag)
{
    std::vector<Candidate> candidates;

    score_calculator_ = score_calculator;
    syanten_type_ = syanten_type;
    dora_indicators_ = dora_indicators;

    calc_syanten_down_ = flag & CalcSyantenDown;
    calc_tegawari_ = flag & CalcTegawari;
    calc_double_reach_ = flag & CalcDoubleReach;
    calc_ippatu_ = flag & CalcIppatu;
    calc_haitei_ = flag & CalcHaiteitumo;
    calc_uradora_ = flag & CalcUradora;
    calc_akatile_tumo_ = flag & CalcAkaTileTumo;
    maximize_win_prob_ = flag & MaximaizeWinProb;

    // 手牌の枚数を数える。
    int n_tiles = hand.num_tiles() + int(hand.melds.size()) * 3;
    if (n_tiles != 14)
        return {false, {}}; // 手牌が14枚ではない場合

    // 現在の向聴数を計算する。
    auto [_, syanten] = SyantenCalculator::calc(hand, syanten_type_);
    if (syanten == -1)
        return {false, {}}; // 手牌が和了形の場合

    // 各牌の残り枚数を数える。
    std::vector<int> counts = count_left_tiles(hand, dora_indicators_);
    sum_left_tiles_ = std::accumulate(counts.begin(), counts.begin() + 34, 0);

    // 自摸確率のテーブルを作成する。
    create_prob_table(sum_left_tiles_);

    if (syanten > 3) // 3向聴以下は聴牌確率、和了確率、期待値を計算する。
        candidates = analyze(syanten, hand);
    else // 4向聴以上は受入枚数のみ計算する。
        candidates = analyze(0, syanten, hand, turn);

    // キャッシュをクリアする。
    clear_cache();

    return {true, candidates};
}

/**
 * @brief 有効牌の一覧を取得する。
 *
 * @param[in] hand 手牌
 * @param[in] syanten_type 向聴数の種類
 * @param[in] counts 各牌の残り枚数
 * @return 有効牌の一覧
 */
std::vector<std::tuple<int, int>>
NaiveExpectedValueCalculator::get_required_tiles(const Hand &hand, int syanten_type,
                                                 const std::vector<int> &counts)
{
    std::vector<std::tuple<int, int>> required_tiles;

    // 有効牌の一覧を取得する。
    std::vector<int> tiles = RequiredTileSelector::select(hand, syanten_type);

    for (auto tile : tiles)
        required_tiles.emplace_back(tile, counts[tile]);

    return required_tiles;
}

/**
 * @brief 各牌の残り枚数を数える。
 *
 * @param[in] hand 手牌
 * @param[in] dora_indicators ドラ表示牌の一覧
 * @return 各牌の残り枚数
 */
std::vector<int>
NaiveExpectedValueCalculator::count_left_tiles(const Hand &hand,
                                               const std::vector<int> &dora_indicators)
{
    std::vector<int> counts(37, 4);
    counts[Tile::AkaManzu5] = counts[Tile::AkaPinzu5] = counts[Tile::AkaSozu5] = 1;

    // 手牌を除く。
    for (int i = 0; i < 34; ++i)
        counts[i] -= hand.num_tiles(i);
    counts[Tile::AkaManzu5] -= hand.aka_manzu5;
    counts[Tile::AkaPinzu5] -= hand.aka_pinzu5;
    counts[Tile::AkaSozu5] -= hand.aka_sozu5;

    // 副露ブロックを除く。
    for (const auto &block : hand.melds) {
        for (auto tile : block.tiles) {
            counts[aka2normal(tile)]--;
            counts[Tile::AkaManzu5] -= tile == Tile::AkaManzu5;
            counts[Tile::AkaPinzu5] -= tile == Tile::AkaPinzu5;
            counts[Tile::AkaSozu5] -= tile == Tile::AkaSozu5;
        }
    }

    // ドラ表示牌を除く。
    for (auto tile : dora_indicators) {
        counts[aka2normal(tile)]--;
        counts[Tile::AkaManzu5] -= tile == Tile::AkaManzu5;
        counts[Tile::AkaPinzu5] -= tile == Tile::AkaPinzu5;
        counts[Tile::AkaSozu5] -= tile == Tile::AkaSozu5;
    }

    return counts;
}

/**
 * @brief 裏ドラ確率のテーブルを初期化する。
 *
 * @return 初期化に成功した場合は true、そうでない場合は false を返す。
 */
bool NaiveExpectedValueCalculator::make_uradora_table()
{
    if (!uradora_prob_table_.empty())
        return true;

    uradora_prob_table_.resize(6);

    boost::filesystem::path path = boost::dll::this_line_location().parent_path() / "uradora.txt";
    std::ifstream ifs(path.string());

    std::string line;
    int i = 0;
    while (std::getline(ifs, line)) {
        std::vector<std::string> tokens;
        boost::split(tokens, line, boost::is_any_of(" "));

        for (auto token : tokens)
            uradora_prob_table_[i].push_back(std::stod(token));
        i++;
    }

    return true;
}

/**
 * @brief 自摸確率のテーブルを初期化する。
 *
 * @param[in] n_left_tiles 1巡目時点の残り枚数の合計
 */
void NaiveExpectedValueCalculator::create_prob_table(int n_left_tiles)
{
    // 有効牌の枚数ごとに、この巡目で有効牌を引ける確率のテーブルを作成する
    // tumo_prob_table_[i][j] = 有効牌の枚数が i 枚の場合に j 巡目に有効牌が引ける確率
    tumo_prob_table_.resize(5, std::vector<double>(17));
    for (int i = 0; i < 5; ++i) {
        for (int j = 0; j < 17; ++j)
            tumo_prob_table_[i][j] = double(i) / double(n_left_tiles - j);
    }

    // 有効牌の合計枚数ごとに、これまでの巡目で有効牌が引けなかった確率のテーブルを作成する
    // not_tumo_prob_table_[i][j] = 有効牌の合計枚数が i 枚の場合に j - 1 巡目までに有効牌が引けなかった確率
    not_tumo_prob_table_.resize(n_left_tiles, std::vector<double>(17));
    for (int i = 0; i < n_left_tiles; ++i) {
        not_tumo_prob_table_[i][0] = 1;
        // n_left_tiles - i - j > 0 は残りはすべて有効牌の場合を考慮
        for (int j = 0; j < 16 && n_left_tiles - i - j > 0; ++j) {
            not_tumo_prob_table_[i][j + 1] = not_tumo_prob_table_[i][j] *
                                             double(n_left_tiles - i - j) /
                                             double(n_left_tiles - j);
        }
    }
}

/**
 * @brief キャッシュをクリアする。
 */
void NaiveExpectedValueCalculator::clear_cache()
{
    // デバッグ用
    // for (size_t i = 0; i < 5; ++i)
    //     spdlog::info("向聴数{} 打牌: {}, 自摸: {}", i, discard_cache_[i].size(),
    //                  draw_cache_[i].size());
    std::for_each(discard_cache_.begin(), discard_cache_.end(), [](auto &x) { x.clear(); });
    std::for_each(draw_cache_.begin(), draw_cache_.end(), [](auto &x) { x.clear(); });
}

/**
 * @brief 自摸牌一覧を取得する。
 *
 * @param[in] hand 手牌
 * @param[in] syanten 手牌の向聴数
 * @param[in] counts 各牌の残り枚数
 * @return 自摸牌候補の一覧。各要素は (牌, 残り枚数, 向聴数の変化) を表す。
 */
std::vector<std::tuple<int, int, int>>
NaiveExpectedValueCalculator::get_draw_tiles(Hand &hand, int syanten,
                                             const std::vector<int> &counts)
{
    std::vector<std::tuple<int, int, int>> flags;
    flags.reserve(34);

    for (int tile = 0; tile < 34; ++tile) {
        if (counts[tile] == 0)
            continue;

        add_tile(hand, tile);
        auto [_, syanten_after] = SyantenCalculator::calc(hand, syanten_type_);
        remove_tile(hand, tile);
        int syanten_diff = syanten_after - syanten; // 向聴数の変化

        if (calc_akatile_tumo_ && tile == Tile::Manzu5 && counts[Tile::AkaManzu5] == 1) {
            // 赤五萬が残っている場合
            if (counts[Tile::Manzu5] >= 2) {
                // 普通の牌と赤牌の両方が残っている
                flags.emplace_back(tile, counts[tile] - 1, syanten_diff);
                flags.emplace_back(Tile::AkaManzu5, 1, syanten_diff);
            }
            else if (counts[Tile::Manzu5] == 1) {
                // 赤牌のみ残っている
                flags.emplace_back(Tile::AkaManzu5, 1, syanten_diff);
            }
        }
        else if (calc_akatile_tumo_ && tile == Tile::Pinzu5 && counts[Tile::AkaPinzu5] == 1) {
            // 赤五筒が残っている場合
            if (counts[Tile::Pinzu5] >= 2) {
                // 普通の牌と赤牌の両方が残っている
                flags.emplace_back(tile, counts[tile] - 1, syanten_diff);
                flags.emplace_back(Tile::AkaPinzu5, 1, syanten_diff);
            }
            else if (counts[Tile::Pinzu5] == 1) {
                // 赤牌のみ残っている
                flags.emplace_back(Tile::AkaPinzu5, 1, syanten_diff);
            }
        }
        else if (calc_akatile_tumo_ && tile == Tile::Sozu5 && counts[Tile::AkaSozu5] == 1) {
            // 赤五索が残っている場合
            if (counts[Tile::Sozu5] >= 2) {
                // 普通の牌と赤牌の両方が残っている
                flags.emplace_back(tile, counts[tile] - 1, syanten_diff);
                flags.emplace_back(Tile::AkaSozu5, 1, syanten_diff);
            }
            else if (counts[Tile::Sozu5] == 1) {
                // 赤牌のみ残っている
                flags.emplace_back(Tile::AkaSozu5, 1, syanten_diff);
            }
        }
        else {
            flags.emplace_back(tile, counts[tile], syanten_diff);
        }
    }

    return flags;
}

/**
 * @brief 打牌一覧を取得する。
 *
 * @param[in] hand 手牌
 * @param[in] syanten 手牌の向聴数
 * @return 打牌一覧 (向聴戻し: 1、向聴数変化なし: 0、手牌に存在しない: inf)
 */
std::vector<int> NaiveExpectedValueCalculator::get_discard_tiles(Hand &hand, int syanten)
{
    std::vector<int> flags(34, std::numeric_limits<int>::max());

    for (int tile = 0; tile < 34; ++tile) {
        if (hand.contains(tile)) {
            remove_tile(hand, tile);
            auto [_, syanten_after] = SyantenCalculator::calc(hand, syanten_type_);
            add_tile(hand, tile);
            flags[tile] = syanten_after - syanten;
        }
    }

    return flags;
}

/**
 * @brief 手牌の点数を取得する。
 *
 * @param[in] hand 手牌
 * @param[in] win_tile 自摸牌
 * @param[in] counts 各牌の残り枚数
 * @return 点数
 */
std::vector<double> NaiveExpectedValueCalculator::get_score(const Hand &hand, int win_tile,
                                                            const std::vector<int> &counts)
{
    // 非門前の場合は自摸のみ
    int hand_flag = hand.is_menzen() ? (HandFlag::Tumo | HandFlag::Reach) : HandFlag::Tumo;

    // 点数計算を行う。
    Result result = score_calculator_.calc(hand, win_tile, hand_flag);

    // 表ドラの数
    int n_dora = int(dora_indicators_.size());

    // ダブル立直、一発、海底撈月で最大3翻まで増加するので、
    // ベースとなる点数、+1翻の点数、+2翻の点数、+3翻の点数も計算しておく。
    std::vector<double> scores(4, 0);
    if (result.success) {
        // 役ありの場合
        std::vector<int> up_scores = score_calculator_.get_scores_for_exp(result);

        if (calc_uradora_ && n_dora == 1) {
            // 裏ドラ考慮ありかつ表ドラが1枚以上の場合は、厳密に計算する。
            std::vector<double> n_indicators(5, 0);
            int sum_indicators = 0;
            for (int tile = 0; tile < 34; ++tile) {
                int n = hand.num_tiles(tile);
                if (n > 0) {
                    // ドラ表示牌の枚数を数える。
                    n_indicators[n] += counts[Dora2Indicator.at(tile)];
                    sum_indicators += counts[Dora2Indicator.at(tile)];
                }
            }

            // 裏ドラの乗る確率を枚数ごとに計算する。
            std::vector<double> uradora_probs(5, 0);

            // 厳密に計算するなら残り枚数は数えるべきだが、あまり影響がないので121枚で固定
            int n_left_tiles = 121;
            uradora_probs[0] = double(n_left_tiles - sum_indicators) / n_left_tiles;
            for (int i = 1; i < 5; ++i)
                uradora_probs[i] = double(n_indicators[i]) / n_left_tiles;

            for (int base = 0; base < 4; ++base) {
                for (int i = 0; i < 5; ++i) { // 裏ドラ1枚の場合、最大4翻まで乗る可能性がある
                    int han_idx = std::min(base + i, int(up_scores.size() - 1));
                    scores[base] += up_scores[han_idx] * uradora_probs[i];
                }
            }
        }
        else if (calc_uradora_ && n_dora > 1) {
            // 裏ドラ考慮ありかつ表ドラが2枚以上の場合、統計データを利用する。
            for (int base = 0; base < 4; ++base) {
                for (int i = 0; i < 13; ++i) {
                    int han_idx = std::min(base + i, int(up_scores.size() - 1));
                    scores[base] += up_scores[han_idx] * uradora_prob_table_[n_dora][i];
                }
            }
        }
        else {
            // 裏ドラ考慮なしまたは表ドラが0枚の場合
            for (int base = 0; base < 4; ++base) {
                int han_idx = std::min(base, int(up_scores.size() - 1));
                scores[base] += up_scores[han_idx];
            }
        }
    }

    return scores;
}

/**
 * @brief 自摸する。(手変わりを考慮しない)
 *
 * @param[in] n_extra_tumo
 * @param[in] syanten 向聴数
 * @param[in] hand 手牌
 * @param[in] counts 各牌の残り枚数
 * @return (各巡目の聴牌確率, 各巡目の和了確率, 各巡目の期待値)
 */
std::tuple<double, double, double>
NaiveExpectedValueCalculator::draw_without_tegawari(int n_extra_tumo, int syanten, Hand &hand,
                                                    std::vector<int> &counts, int turn)
{
    assert(syanten > -1);
    // 自摸候補を取得する。
    std::vector<std::tuple<int, int, int>> flags = get_draw_tiles(hand, syanten, counts);

    // 有効牌の合計枚数を計算する。
    int sum_required_tiles = 0;
    for (auto &[tile, count, diff] : flags) {
        if (diff == -1) // 有効牌の場合
            sum_required_tiles += count;
    }

    double tenpai_prob = 0, win_prob = 0, exp_value = 0;
    for (auto &[tile, count, diff] : flags) {
        if (diff != -1) // 有効牌以外の場合
            continue;

        const std::vector<double> &tumo_probs = tumo_prob_table_[count];
        const std::vector<double> &not_tumo_probs = not_tumo_prob_table_[sum_required_tiles];

        // 手牌に加える
        add_tile(hand, tile, counts);

        std::vector<double> scores;
        if (syanten == 0) {
            scores = get_score(hand, tile, counts);
        }

        for (int t = turn - 1; t <= 16; ++t) {
            // 現在の巡目が turn の場合に t 巡目に有効牌を引く確率
            double not_prob = not_tumo_probs[t] / not_tumo_probs[turn - 1];
            double prob = tumo_probs[t] * not_prob;

            double tumo_prob2 = double(count) / (sum_left_tiles_ - t);
            double not_prob2 = 1;
            for (int j = turn - 1; j < t; j++)
                not_prob2 *=
                    double(sum_left_tiles_ - j - sum_required_tiles) / (sum_left_tiles_ - j);

            assert(std::abs(tumo_probs[t] - tumo_prob2) < 10e-10);
            assert(std::abs(not_prob - not_prob2) < 10e-10);
            //std::cout << fmt::format("tumo_prob t={}, {} vs {}\n", t, tumo_probs[t], prob2);
            // std::cout << fmt::format("not_tumo_prob turn={}, t={}, {} vs {}\n", turn, t, not_prob,
            //                          not_prob2);

            double next_tenpai_prob = 0, next_win_prob = 0, next_exp_value = 0;
            if (t < 16 && syanten > 0) {
                // 次の巡目がある場合
                std::tie(next_tenpai_prob, next_win_prob, next_exp_value) =
                    discard(n_extra_tumo, syanten - 1, hand, counts, t + 2);
            }

            if (syanten == 1) // 1向聴の場合は次で聴牌
                tenpai_prob += prob;
            else if (t < 16 && syanten > 1) // 2向聴以上で16巡目以下の場合
            {
                tenpai_prob += prob * next_tenpai_prob;
                // if (syanten - (16 - t) > 1) {
                //     // 聴牌までたどり着くことが不可能
                //     assert(next_tenpai_prob == 0);
                // }
                // else {
                //     assert(next_tenpai_prob > 0);
                // }
            }

            // scores[0] == 0 の場合は役なしなので、和了確率、期待値は0
            if (syanten == 0 && scores[0] != 0) { // 聴牌の場合は次で和了
                // 1巡目で聴牌の場合はダブル立直成立
                bool win_double_reach = turn == 1 && calc_double_reach_;
                // 1巡目で聴牌し、次の巡目で和了の場合は一発成立
                bool win_ippatu = t == turn - 1 && calc_ippatu_;
                // 最後の巡目で和了の場合は海底撈月成立
                bool win_haitei = t == 16 && calc_haitei_;

                win_prob += prob;
                exp_value += prob * scores[win_double_reach + win_ippatu + win_haitei];
            }
            else if (t < 16 && syanten > 0) { // 聴牌以上で16巡目以下の場合
                win_prob += prob * next_win_prob;
                exp_value += prob * next_exp_value;
            }
        }

        // 手牌から除く
        remove_tile(hand, tile, counts);
    }

    return {tenpai_prob, win_prob, exp_value};
}

/**
 * @brief 自摸する。(手変わりを考慮する)
 *
 * @param[in] n_extra_tumo
 * @param[in] syanten 向聴数
 * @param[in] hand 手牌
 * @param[in] counts 各牌の残り枚数
 * @return (各巡目の聴牌確率, 各巡目の和了確率, 各巡目の期待値)
 *
 * この関数が呼ばれた時点で向聴戻しは行われていない
 */
std::tuple<double, double, double>
NaiveExpectedValueCalculator::draw_with_tegawari(int n_extra_tumo, int syanten, Hand &hand,
                                                 std::vector<int> &counts, int turn)
{
    assert(syanten > -1);
    // 自摸候補を取得する。
    std::vector<std::tuple<int, int, int>> flags = get_draw_tiles(hand, syanten, counts);

    // 有効牌の合計枚数を計算する。
    double s = 0;
    int s2 = 0;
    int s3 = 0;
    // 残り牌を集計
    for (int i = 0; i < 34; ++i) {
        s2 += counts[i];
    }

    for (auto &[tile, count, syanten_diff] : flags) {
        s += double(count) / s2;
        s3 += count;
    }

    // if (s2 != 121 - turn - 1) {
    //     std::cout << fmt::format("121 - turn - 1={}, s={}, diff={}", 121 - turn - 1, s3,
    //                              (121 - turn - 1) - s3)
    //               << std::endl;
    // }

    // if (std::abs(s - 1) > 10e-10) {
    //     std::cout << fmt::format("t={}, sum={}", turn - 1, s2) << std::endl;
    //     std::cout << s << " " << std::endl;
    //     for (auto &[tile, count, syanten_diff] : flags) {
    //         std::cout << fmt::format("count={}, prob={:.8f}", count,
    //                                  tumo_prob_table_[count][turn - 1])
    //                   << std::endl;
    //     }
    //     assert(std::abs(s - 1) < 10e-10);
    // }

    assert(std::abs(s - 1) < 10e-10);

    double tenpai_prob = 0, win_prob = 0, exp_value = 0;
    for (auto &[tile, count, syanten_diff] : flags) {
        assert(syanten_diff == -1 || syanten_diff == 0);
        if (syanten_diff != -1)
            continue; // 有効牌以外の場合

        const std::vector<double> &tumo_probs = tumo_prob_table_[count];

        // 手牌に加える
        add_tile(hand, tile, counts);

        std::vector<double> scores;
        if (syanten == 0) {
            scores = get_score(hand, tile, counts);
        }

        int t = turn - 1;
        double next_tenpai_prob = 0, next_win_prob = 0, next_exp_value = 0;
        if (t < 16 && syanten > 0) {
            std::tie(next_tenpai_prob, next_win_prob, next_exp_value) =
                discard(n_extra_tumo, syanten - 1, hand, counts, turn + 1);
        }

#ifdef FIX_TEGAWARI_PROB
        double tumo_prob = double(count) / s3; // 確率が1を超えないようにするため
#else
        double tumo_prob = tumo_probs[t];
#endif

        if (syanten == 1) // 1向聴の場合は次で聴牌
            tenpai_prob += tumo_prob;
        else if (t < 16 && syanten > 1)
            tenpai_prob += tumo_prob * next_tenpai_prob;

        if (syanten == 0) { // 聴牌の場合は次で和了
            // 1巡目で聴牌の場合はダブル立直成立
            bool win_double_reach = turn == 1 && calc_double_reach_;
            // 1巡目で聴牌し、次の巡目で和了の場合は一発成立
            bool win_ippatu = t == turn - 1 && calc_ippatu_;
            // 最後の巡目で和了の場合は海底撈月成立
            bool win_haitei = t == 16 && calc_haitei_;

            win_prob += tumo_prob;
            exp_value += tumo_prob * scores[win_double_reach + win_ippatu + win_haitei];
        }
        else if (t < 16 && syanten > 0) {
            win_prob += tumo_prob * next_win_prob;
            exp_value += tumo_prob * next_exp_value;
        }

        // 手牌から除く
        remove_tile(hand, tile, counts);
    }

    for (auto &[tile, count, syanten_diff] : flags) {
        if (syanten_diff != 0)
            continue; // 有効牌の場合

        const std::vector<double> &tumo_probs = tumo_prob_table_[count];
        int t = turn - 1;
#ifdef FIX_TEGAWARI_PROB
        double tumo_prob = double(count) / s3; // 確率が1を超えないようにするため
#else
        double tumo_prob = tumo_probs[t];
#endif

        // 手牌に加える
        add_tile(hand, tile, counts);

        double next_tenpai_prob = 0, next_win_prob = 0, next_exp_value = 0;
        if (t < 16) {
            auto [next_tenpai_prob, next_win_prob, next_exp_value] =
                discard(n_extra_tumo + 1, syanten, hand, counts, turn + 1);
            tenpai_prob += tumo_prob * next_tenpai_prob;
            win_prob += tumo_prob * next_win_prob;
            exp_value += tumo_prob * next_exp_value;
        }

        // 手牌から除く
        remove_tile(hand, tile, counts);
    }

    return {tenpai_prob, win_prob, exp_value};
}

/**
 * @brief 自摸する。
 *
 * @param[in] n_extra_tumo
 * @param[in] syanten 向聴数
 * @param[in] hand 手牌
 * @param[in] counts 各牌の残り枚数
 * @return (各巡目の聴牌確率, 各巡目の和了確率, 各巡目の期待値)
 */
std::tuple<double, double, double> NaiveExpectedValueCalculator::draw(int n_extra_tumo, int syanten,
                                                                      Hand &hand,
                                                                      std::vector<int> &counts,
                                                                      int turn)
{
    if (calc_tegawari_ && n_extra_tumo == 0)
        return draw_with_tegawari(n_extra_tumo, syanten, hand, counts, turn);
    else
        return draw_without_tegawari(n_extra_tumo, syanten, hand, counts, turn);
}

/**
 * @brief 打牌する。
 *
 * @param[in] n_extra_tumo
 * @param[in] syanten 向聴数
 * @param[in] hand 手牌
 * @param[in] counts 各牌の残り枚数
 * @return (各巡目の聴牌確率, 各巡目の和了確率, 各巡目の期待値)
 */
std::tuple<double, double, double> NaiveExpectedValueCalculator::discard(int n_extra_tumo,
                                                                         int syanten, Hand &hand,
                                                                         std::vector<int> &counts,
                                                                         int turn)
{
    assert(syanten > -1);

    // 打牌候補を取得する。
    const std::vector<int> flags = get_discard_tiles(hand, syanten);

    // 期待値が最大となる打牌を選択する。
    double max_tenpai_prob, max_win_prob, max_exp_value;
    double tenpai_prob, win_prob, exp_value;
    int max_tile = -1;
    double max_value = -1;
    for (int tile = 0; tile < 34; ++tile) {
        int discard_tile = tile;
        // 赤牌以外が残っている場合はそちらを先に捨てる。
        if (tile == Tile::Manzu5 && hand.aka_manzu5 && hand.num_tiles(Tile::Manzu5) == 1)
            discard_tile = Tile::AkaManzu5;
        else if (tile == Tile::Pinzu5 && hand.aka_pinzu5 && hand.num_tiles(Tile::Pinzu5) == 1)
            discard_tile = Tile::AkaPinzu5;
        else if (tile == Tile::Sozu5 && hand.aka_sozu5 && hand.num_tiles(Tile::Sozu5) == 1)
            discard_tile = Tile::AkaSozu5;

        if (flags[tile] == 0) {
            // 向聴数が変化しない打牌
            remove_tile(hand, discard_tile);
            std::tie(tenpai_prob, win_prob, exp_value) =
                draw(n_extra_tumo, syanten, hand, counts, turn);
            add_tile(hand, discard_tile);
        }
        else if (calc_syanten_down_ && n_extra_tumo == 0 && flags[tile] == 1) {
            // 向聴戻ししたら最短でも聴牌できなくなる場合
            // if (turn + syanten > 18)
            //     continue; //

            // 向聴戻しになる打牌
            remove_tile(hand, discard_tile);
            std::tie(tenpai_prob, win_prob, exp_value) =
                draw(n_extra_tumo + 1, syanten + 1, hand, counts, turn);
            add_tile(hand, discard_tile);
        }
        else {
            // 手牌に存在しない牌、または向聴落としが無効な場合に向聴落としとなる牌
            continue;
        }

        // 向聴戻し、手変わりは巡目がずれるので、1つ手前にずらす。(このやり方で正しいのか要検証)

        // 和了確率は下2桁まで一致していれば同じ、期待値は下0桁まで一致していれば同じとみなす。
        double value = maximize_win_prob_ ? int(win_prob * 10000) : int(exp_value);
        if ((value == max_value && DiscardPriorities[max_tile] < DiscardPriorities[tile]) ||
            value > max_value) {
            // 値が同等なら、DiscardPriorities が高い牌を優先して選択する。
            max_tenpai_prob = tenpai_prob;
            max_win_prob = win_prob;
            max_exp_value = exp_value;

            max_value = value;
            max_tile = tile;
        }
    }

    return {max_tenpai_prob, max_win_prob, max_exp_value};
}

/**
 * @brief 手牌の推移パターンを和了まですべて解析する。
 *
 * @param [in]n_extra_tumo
 * @param [in]syanten 向聴数
 * @param [in]_hand 手牌
 * @return std::vector<Candidate> 打牌候補の一覧
 */
std::vector<Candidate> NaiveExpectedValueCalculator::analyze(int n_extra_tumo, int syanten,
                                                             const Hand &_hand, int turn)
{
    assert(syanten >= -1);

    std::vector<Candidate> candidates;
    Hand hand = _hand;

    // 各牌の残り枚数を数える。
    std::vector<int> counts = count_left_tiles(hand, dora_indicators_);

    // 打牌候補を取得する。
    const std::vector<int> flags = get_discard_tiles(hand, syanten);

    for (int tile = 0; tile < 34; ++tile) {
        int discard_tile = tile;
        // 赤牌以外が残っている場合はそちらを先に捨てる。
        if (tile == Tile::Manzu5 && hand.aka_manzu5 && hand.num_tiles(Tile::Manzu5) == 1)
            discard_tile = Tile::AkaManzu5;
        else if (tile == Tile::Pinzu5 && hand.aka_pinzu5 && hand.num_tiles(Tile::Pinzu5) == 1)
            discard_tile = Tile::AkaPinzu5;
        else if (tile == Tile::Sozu5 && hand.aka_sozu5 && hand.num_tiles(Tile::Sozu5) == 1)
            discard_tile = Tile::AkaSozu5;

        if (flags[tile] == 0) {
            remove_tile(hand, discard_tile);

            auto required_tiles = get_required_tiles(hand, syanten_type_, counts);

            auto [tenpai_prob, win_prob, exp_value] =
                draw(n_extra_tumo, syanten, hand, counts, turn);

            if (syanten == 0)
                tenpai_prob = 1;

            add_tile(hand, discard_tile);

            std::vector<double> tenpai_probs(17), win_probs(17), exp_values(17);
            tenpai_probs[turn - 1] = tenpai_prob;
            win_probs[turn - 1] = win_prob;
            exp_values[turn - 1] = exp_value;
            candidates.emplace_back(discard_tile, required_tiles, tenpai_probs, win_probs,
                                    exp_values, false);
        }
        else if (calc_syanten_down_ && flags[tile] == 1 && n_extra_tumo == 0 && syanten < 3) {
            remove_tile(hand, discard_tile);

            auto required_tiles = get_required_tiles(hand, syanten_type_, counts);

            auto [tenpai_prob, win_prob, exp_value] =
                draw(n_extra_tumo + 1, syanten + 1, hand, counts, turn);

            add_tile(hand, discard_tile);

            std::vector<double> tenpai_probs(17), win_probs(17), exp_values(17);
            tenpai_probs[turn - 1] = tenpai_prob;
            win_probs[turn - 1] = win_prob;
            exp_values[turn - 1] = exp_value;

            candidates.emplace_back(discard_tile, required_tiles, tenpai_probs, win_probs,
                                    exp_values, true);
        }
    }

    return candidates;
}

/**
 * @brief 手牌の推移パターンを1手先まで解析する。
 *
 * @param [in]syanten 向聴数
 * @param [in]_hand 手牌
 * @return std::vector<Candidate> 打牌候補の一覧
 */
std::vector<Candidate> NaiveExpectedValueCalculator::analyze(int syanten, const Hand &_hand)
{
    std::vector<Candidate> candidates;
    Hand hand = _hand;

    // 各牌の残り枚数を数える。
    std::vector<int> counts = count_left_tiles(hand, dora_indicators_);

    std::vector<int> tiles = UnnecessaryTileSelector::select(hand, syanten_type_);

    for (auto tile : tiles) {
        remove_tile(hand, tile);
        auto required_tiles = get_required_tiles(hand, syanten_type_, counts);
        auto [_, syanten_after] = SyantenCalculator::calc(hand, syanten_type_);
        bool syanten_down = syanten_after == 1;
        add_tile(hand, tile);
        candidates.emplace_back(tile, required_tiles, syanten_down);
    }

    return candidates;
}

std::vector<std::vector<double>> NaiveExpectedValueCalculator::uradora_prob_table_;

} // namespace mahjong


/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2021
 * Alberto Francisco Kummer Neto (afkneto@inf.ufrgs.br),
 * Luciana Salete Buriol (buriol@inf.ufrgs.br),
 * Olinto César Bassi de Araújo (olinto@ctism.ufsm.br) and
 * Mauricio G.C. Resende (resendem@amazon.com)
 *
 * 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 "Instance.h"
#include "SortingDecoder.h"
#include "Timer.h"

#include "brkga_mp_ipr.hpp"

#include <cstdlib>
#include <iomanip>
#include <iostream>
#include <stdexcept>
#include <string>
#include <sstream>

#include <boost/program_options.hpp>

using namespace std;

/// Parses the command line arguments using boost::program_options library.
boost::program_options::variables_map parseCommandline(int argc, char **argv);

/// Converts the parameters to the format that BRKGA library uses.
BRKGA::BrkgaParams extractFrom(boost::program_options::variables_map &vm);

/// Prints some metrics regarding the number of qualified caregivers,
/// and the number of service requests, per service type.
void printSupplyDemandIndicators(const Instance &inst);

/// Computes the average and standard deviation of elite set of the GA.
tuple<double, double> computeEliteDiversity(const BRKGA::BRKGA_MP_IPR<SortingDecoder> &solver);

int main(int argc, char* argv[]) {
   // Parses command line arguments.
   auto args = parseCommandline(argc, argv);
   auto brkga_params = extractFrom(args);

   // Print some information about the run.
   const unsigned seed = args["seed"].as<int>();
   cout << "--- BRKGA-MP-IPR for HHCRSP ---\n";
   cout << "Instance: " << args["instance"].as<string>() << endl;
   cout << "Seed: " << seed << endl;
   cout << "\n";

   // Reads the problem instance.
   const string instFile = args["instance"].as<string>();
   cout << "Parsing instance file...\n";
   auto instance = Instance(instFile.c_str());
   cout << "Problem contains " << (instance.numNodes() - 2) <<
      " patients and " << instance.numVehicles() << " caregivers.\n";
   printSupplyDemandIndicators(instance);
   cout << endl;

   double overallBest = 1e75;
   try {
      // Caches a copy some of the control parameters.
      const unsigned num_generations = args["gens"].as<int>();
      const int immigrants = args["immigrants"].as<int>();
      const auto iprType = args["ptype"].as<string>();

      // Logic for selecting the distance function according to
      // implicit PR selection.
      shared_ptr <BRKGA::DistanceFunctionBase> distFuncPtr;
      {
         const auto &str = args["dfunc"].as<string>();
         if (str.empty()) {
            cout << "Inferred distance function by PR type: ";
            if (str == "direct") {
               cout << "hamming";
               distFuncPtr.reset(new BRKGA::HammingDistance(args["hdist"].as<int>()));
            } else {
               cout << "kendall-tau";
               distFuncPtr.reset(new BRKGA::KendallTauDistance());
            }
            cout << "\n";
         } else {
            if (str == "hamming") {
               cout << "Using hamming distance forcibly.\n";
               distFuncPtr.reset(new BRKGA::HammingDistance(args["hdist"].as<int>()));
            } else if (str == "kendall") {
               cout << "Using kendall-tau distance forcibly.\n";
               distFuncPtr.reset(new BRKGA::KendallTauDistance());
            } else {
               cout << "Unknown distance function: " << str << "\n";
               abort();
            }
         }
      }

      cout << "Initializing genetic algorithm...\n";
      SortingDecoder decoder(instance);
      BRKGA::BRKGA_MP_IPR<SortingDecoder> algorithm(
         decoder, BRKGA::Sense::MINIMIZE, seed,
         decoder.chromosomeLength(), brkga_params, omp_get_max_threads()
      );
      algorithm.initialize();

      // Some other control parameters used within the algorithm.
      const auto prPeriod = args["pperiod"].as<int>();
      const auto resetPeriod = args["reset"].as<long>();
      const auto exchangePeriod = args["xelite"].as<int>();
      const auto printPeriod = args.count("printall") ? 1 : 50;

      // Some indicators collected during the run.
      int iprHomogeneous = 0, iprNoImprovement = 0,
         iprEliteImprovement = 0, iprBestImprovement = 0;
      int linesPrinted = 0;
      int opIpr = 0, opXe = 0, opRst = 0;

      Timer tm;
      unsigned generation = 0;
      int noImprove = 0;

      double localBest = numeric_limits<double>::infinity();
      double dist = localBest, tard = localBest, tmax = localBest;

      // Prints the header of algorithm output.
      auto printHeader = [] () {
         cout << "\n";
         cout <<
            setw(1) << "" << " " <<
            setw(4) << "Gens" << " " <<
            setw(4) << "Rem" << " " <<

            setw(8) << "Local" << " " <<
            setw(7) << "ElDiv" << " " <<
            setw(3) << "NoImpr" << " " <<

            setw(33) << "Best solution        " << " " <<

            setw(7) << "Time" << " " <<
            setw(3) << "Op" <<
         "\n";

         cout <<
            setw(34) << "" << " " <<
            setw(8) << "Cost" << " " <<
            setw(8) << "Dist" << " " <<
            setw(7) << "Tard" << " " <<
            setw(7) << "TMax" << " " <<
         "\n";

      };

      // Prints the algorithm progress.
      char hdr = ' ';
      string evt = "";
      auto printProgress = [&] () {
         if (linesPrinted >= 15) {
            printHeader();
            linesPrinted = 0;
         } else {
            ++linesPrinted;
         }

         auto [eliteMean, eliteStdev] = computeEliteDiversity(algorithm);
         tm.finish();
         cout <<
            fixed <<
            setw(1) << hdr << " " <<
            setw(4) << generation << " " <<
            setw(4) << num_generations - generation << " " <<

            setw(8) << setprecision(2) << localBest << " " <<
            setw(7) << setprecision(2) << eliteStdev << " " <<
            setw(6) << noImprove << " " <<

            setw(8) << setprecision(2) << overallBest << " " <<
            setw(8) << setprecision(2) << dist << " " <<
            setw(7) << setprecision(1) << tard << " " <<
            setw(7) << setprecision(1) << tmax << " " <<

            setw(7) << setprecision(1) << tm.elapsed() << " " <<
            setw(3) << evt <<
         "\n";
         hdr = ' ';
         evt = "";
      };

      cout << "Stopping criteria: " << instance.numNodes()/2 << " staled generations, maximum of "<< num_generations << " generations.\n";

      printHeader();
      tm.start();
      do {
         bool hasImproved = false;
         algorithm.evolve();

         if (localBest >= numeric_limits<double>::infinity()) {
            localBest = algorithm.getBestFitness();
            hasImproved = true;
         }

         if (localBest < overallBest) {
            overallBest = localBest;
            const auto sol = decoder.decodeSolution(algorithm.getBestChromosome());
            dist = sol.dist;
            tard = sol.tard;
            tmax = sol.tmax;
            hdr = '*';
         }

         if (generation % printPeriod == 0 || hasImproved || hdr == '*') {
            printProgress();
         }

         if (prPeriod > 0 && generation > 0 && generation % prPeriod == 0) {
            using BRKGA::PathRelinking::PathRelinkingResult;
            evt += 'P';
            opIpr++;
            PathRelinkingResult result = algorithm.pathRelink(distFuncPtr);
            cout << "Path relinking result: ";
            switch(result) {
               case PathRelinkingResult::TOO_HOMOGENEOUS:
                  cout << "too homogeneous.";
                  iprHomogeneous++;
                  break;
               case PathRelinkingResult::NO_IMPROVEMENT:
                  cout << "no improvement.";
                  iprNoImprovement++;
                  break;
               case PathRelinkingResult::ELITE_IMPROVEMENT:
                  cout << "elite improvement.";
                  iprEliteImprovement++;
                  break;
               case PathRelinkingResult::BEST_IMPROVEMENT:
                  cout << "best improvement.";
                  iprBestImprovement++;
                  break;
            }
            cout << "\n";
         }

         if (exchangePeriod > 0 && generation > 0 && brkga_params.num_independent_populations > 1 && generation % exchangePeriod == 0) {
            evt += 'X';
            opXe++;
            algorithm.exchangeElite(immigrants);
         }

         if (localBest - algorithm.getBestFitness() < 0.05) {
            ++noImprove;
         } else {
            localBest = algorithm.getBestFitness();
            noImprove = 0;
         }

         if (noImprove >= resetPeriod) {
            evt += 'R';
            opRst++;
            localBest = numeric_limits<double>::infinity();
            algorithm.reset();
            noImprove = 0;
         }

         if (!evt.empty()) {
            printProgress();
         }

         ++generation;

         // New stopping criteria: by iterations without improvement.
         if (noImprove >= instance.numNodes()/2) {
            cout << "Stopping a stale search.\n";
            break;
         }
      } while (generation < num_generations);

      printProgress();
      cout << "\nEvolutionary process finished.\n";

      // Post-optimization processing of the fittest individual.
      {
         char buf[256] = "solution-XXXXXX.txt";
         int fid = mkstemps(buf, 4);
         if (fid == -1) {
            cout << "Error creating solution file: " << strerror(errno) << endl;
            return EXIT_FAILURE;
         }
         close(fid);
         auto sol = decoder.decodeSolution(algorithm.getBestChromosome());
         sol.writeFile(buf, seed);
         cout << "Solution written to '" << buf << "'.\n";

         cout << "Unused vehicle list: ";
         int idle = 0;
         for (int v = 0; v < instance.numVehicles(); ++v) {
            if (sol.routes[v].size() <= 2) {
               ++idle;
               cout << v << ' ';
            }
         }
         cout << "(total = " << idle << " out of " << instance.numVehicles() << ")" << "\n";
      }

      cout << "\n---\nSearch finished.\n";
      cout << "Total of " << generation << " generations in " << tm.elapsed() << " seconds.\n";
      cout << "Exchange elite runs: " << opXe << "\n";
      cout << "Implicit path relinking runs: " << opIpr << "\n";
      cout << "Reset attempts: " << opRst << "\n";
      cout << "\n";

      cout << "Best solution found:\n";
      cout << "   Cost: " << overallBest << "\n";
      cout << "   Total travel time: " << dist << "\n";
      cout << "   Total tardiness time: " << tard << "\n";
      cout << "   Largest tardiness: " << tmax << "\n";
   }
   catch(exception& e) {
      cerr << "\n***********************************************************"
           << "\n****  Exception Occured: " << e.what()
           << "\n***********************************************************"
           << endl;
      return 70; // BSD software internal error code
   }

   cout << "\n\n" << overallBest << endl;

   return 0;
}

boost::program_options::variables_map parseCommandline(int argc, char **argv) {
   namespace po = boost::program_options;
   po::options_description desc("Accepted command options are");
   desc.add_options()
      ("help,h", "shows this text")

      ("instance,i", po::value<string>(), "path to the instance file")

      ("seed,s", po::value<int>()->default_value(1), "seed for the PRNG")

      ("printall", "log the search progress in each generation, otherwise from "
         "50 to 50 generations, or when a improved solution is found")

      ("popsize,p", po::value<int>()->default_value(300), "number of individuals "
       "in each population")

      ("gens,g", po::value<int>()->default_value(99999),
       "max. generations for the BRKGA phase")

      ("pe", po::value<double>()->default_value(0.15), "percentage of elite population")

      ("pm", po::value<double>()->default_value(0.15), "percentagem of mutant population")

      ("mp", po::value<int>()->default_value(2), "number of parents involved in the mating")

      ("ep", po::value<int>()->default_value(1), "number of elite parents involved in the mating")

      ("bp", po::value<string>()->default_value("loginverse"), "set the bias"
       " function to be used while selecting parents for mating. Accepted "
       "values: constant, cubic, exponential, linear, loginverse, quadratic")

      ("npop", po::value<int>()->default_value(1), "number of independent populations to evolve")

      ("npairs", po::value<int>()->default_value(100), "number of pairs of chromosomes to test during PR")

      ("mindist", po::value<double>()->default_value(0.15), "minimal distance between chromosomes to allow "
       "performing PR")

      ("psel", po::value<string>()->default_value("best"), "specifies which individuals used durint the PR. "
       "Valid options: best, random")

      ("alpha", po::value<double>()->default_value(1.0), "defines the block size during PR")

      ("hdist", po::value<double>()->default_value(0.5), "defines the thresold value, used in the hamming distance "
         "to interpret a random key as false or true")

      ("pperc", po::value<double>()->default_value(1.0), "defines the percentage of PR path to explore")

      ("ptype", po::value<string>()->default_value("permutation"), "type of PR. Valid values: direct, permutation")

      ("dfunc", po::value<string>()->default_value(""), "type of distance function to be used. Accepted values are hamming and kendall. If the parameter is suppressed, it is set automatically according to value of ptype: permutation => kendall, direct => hamming")

      ("pperiod", po::value<int>()->default_value(50), "number of generations between PR attempts")

      ("reset", po::value<long>()->default_value(1e6), "number of non-improving generations prior resetting the populations")

      ("xelite", po::value<int>()->default_value(100), "number of generations between exchanging elite from populations")

      ("immigrants", po::value<int>()->default_value(8), "number of immigrants while exchanging elites")
   ;

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

   if (vm.count("help") || !vm.count("instance")) {
      cout << desc << "\n";
      exit(EXIT_FAILURE);
   }

   return vm;
}

BRKGA::BrkgaParams extractFrom(boost::program_options::variables_map &vm) {
   auto params {BRKGA::BrkgaParams()};

   params.population_size = vm["popsize"].as<int>();
   params.elite_percentage = vm["pe"].as<double>();
   params.mutants_percentage = vm["pm"].as<double>();
   params.total_parents = vm["mp"].as<int>();
   params.num_elite_parents = vm["ep"].as<int>();

   {
      auto bp = vm["bp"].as<string>();
      if (bp == "constant") {
         params.bias_type = BRKGA::BiasFunctionType::CONSTANT;
      } else if (bp == "cubic") {
         params.bias_type = BRKGA::BiasFunctionType::CUBIC;
      } else if (bp == "exponential") {
         params.bias_type = BRKGA::BiasFunctionType::EXPONENTIAL;
      } else if (bp == "linear") {
         params.bias_type = BRKGA::BiasFunctionType::LINEAR;
      } else if (bp == "loginverse") {
         params.bias_type = BRKGA::BiasFunctionType::LOGINVERSE;
      } else if (bp == "quadratic") {
         params.bias_type = BRKGA::BiasFunctionType::QUADRATIC;
      } else {
         cout << "Invalid bias for parent selection: " << bp << "\n";
         exit(EXIT_FAILURE);
      }
   }

   params.num_independent_populations = vm["npop"].as<int>();
   params.pr_number_pairs = vm["npairs"].as<int>();
   params.pr_minimum_distance = vm["mindist"].as<double>();

   {
      const auto ty = vm["ptype"].as<string>();
      if (ty == "direct") {
         params.pr_type = BRKGA::PathRelinking::Type::DIRECT;
      } else if (ty == "permutation") {
         params.pr_type = BRKGA::PathRelinking::Type::PERMUTATION;
      } else {
         cout << "Unknown path relinking type: " << ty << "\n";
         exit(EXIT_FAILURE);
      }
   }

   {
      auto sel = vm["psel"].as<string>();
      if (sel == "best") {
         params.pr_selection = BRKGA::PathRelinking::Selection::BESTSOLUTION;
      } else if (sel == "random") {
         params.pr_selection = BRKGA::PathRelinking::Selection::RANDOMELITE;
      } else {
         cout << "Invalid individual selection for PR: " << sel << "\n";
         exit(EXIT_FAILURE);
      }
   }

   params.alpha_block_size = vm["alpha"].as<double>();
   params.pr_percentage = vm["pperc"].as<double>();

   return params;
}

void printSupplyDemandIndicators(const Instance &inst) {
   cout << "Summary of supply/demand per service type:\n";
   for (int s = 0; s < inst.numSkills(); ++s) {
      int supply = 0, demand = 0;
      for (int i = 1; i < inst.numNodes()-1; ++i) {
         demand += inst.nodeReqSkill(i, s) ? 1 : 0;
      }
      for (int v = 0; v < inst.numVehicles(); ++v) {
         supply += inst.vehicleHasSkill(v, s) ? 1 : 0;
      }
      cout << "   Service type: " << s << " Qualified carers: " << setw(3) << supply <<
         " Service requests: " << setw(4) << demand << " Ratio = " <<  double(supply)/demand<< "\n";
   }
}

tuple<double, double> computeEliteDiversity(const BRKGA::BRKGA_MP_IPR<SortingDecoder> &solver) {
   int cnt = 0;
   double mean = 0.0;
   const auto npop = solver.getBrkgaParams().num_independent_populations;
   const auto numElites = solver.getBrkgaParams().population_size * solver.getBrkgaParams().elite_percentage;
   for (unsigned k = 0; k < npop; ++k) {
      for (int i = 0; i < numElites; ++i) {
         mean += solver.getFitness(k, i);
         ++cnt;
      }
   }
   mean /= cnt;
   double stdev = 0.0;
   for (unsigned k = 0; k < npop; ++k) {
      for (int i = 0; i < numElites; ++i) {
         stdev += pow(solver.getFitness(k, i) - mean, 2.0);
      }
   }
   stdev /= cnt;
   stdev = sqrt(stdev);
   return make_tuple(mean, stdev);
}


/*=============================================================================
    Spirit v1.6.0
    Copyright (c) 2001-2003 Andy Elvey
    Copyright (c) 2001-2003 Dan Nuffer
    http://spirit.sourceforge.net/

    Permission to copy, use, modify, sell and distribute this software is
    granted provided this copyright notice appears in all copies. This
    software is provided "as is" without express or implied warranty, and
    with no claim as to its suitability for any purpose.
=============================================================================*/
//
//  A very simple parser grammar .
//  This parser parses a simple polynomial expression ( of the form
//  aX^2 + bX^3 + ...  ) .
//
// Uses:  The Spirit parser framework, which was written by
//  Joel de Guzman isis-tech.n3.net
//

//#define BOOST_SPIRIT_DEBUG  ///$$$ DEFINE THIS WHEN DEBUGGING $$$///

#include <boost/spirit/core.hpp>
#include <iostream>
#include <fstream>
#include <vector>
#include <string>

///////////////////////////////////////////////////////////////////////////////
using namespace boost::spirit;
using namespace std;

///////////////////////////////////////////////////////////////////////////////
//----------------------------------------------------------------------------
//  Start grammar definition
//----------------------------------------------------------------------------

struct polynomial_grammar :
    public grammar<polynomial_grammar>
{
    template <typename ScannerT>
    struct definition
    {
        typedef rule<ScannerT> rule_t;

        rule_t program, stmts, term, constant, sign, var, caret;
        rule_t IDENT, STRING_LITERAL, VAR;

        definition(polynomial_grammar const&)
        {
            //-----------------------------------------------------------------
            // OPERATORS
            //-----------------------------------------------------------------
            chlit<>     PLUS('+');
            chlit<>     MINUS('-');
            chlit<>     CARET('^');

            //-----------------------------------------------------------------
            // TOKENS
            //-----------------------------------------------------------------

            IDENT =
                as_lower_d
                [
                    lexeme_d
                    [
                        (alpha_p >> *(alnum_p | '_'))
                    ]
                ]
            ;

            STRING_LITERAL =
                lexeme_d
                [
                    ch_p('\'')
                    >>  +(
                            str_p("\'\'")
                            | anychar_p - ch_p('\'')
                         )
                    >> ch_p('\'')
                ]
            ;

            VAR = as_lower_d[ lexeme_d[ alpha_p ] ] ;

            //-----------------------------------------------------------------
            // RULES
            //-----------------------------------------------------------------

            //  Now - the actual BNF grammar for the parser

            program =
                +(stmts)
            ;

            stmts =
                ( term | constant )
            ;

            term =
                +(
                    !(sign)
                    >> !(uint_p)
                    >> VAR
                    >> !(CARET)
                    >> !(uint_p)
                 )
            ;   //  e.g. 3x^2

            constant =
                !(sign) >> +(uint_p)
            ;

            sign =
                ( PLUS | MINUS )
            ;

            var =
                VAR
            ;

            caret =
                CARET
            ;

        }

        rule_t const& start() const
        {
            return program;
        }
    };
};

int
main(int /*argc*/, char* /*argv[]*/)
{
    cout << "/////////////////////////////////////////////////////////\n\n";
    cout << "\t\t A polynomial parser...\n\n";
    cout << "/////////////////////////////////////////////////////////\n\n";
    cout << "Type a polynomial (e.g. 3a^4 + 7a^5 - 8a^3 - 4a - 5) or\n"
        << "[q or Q] to quit\n\n";

    polynomial_grammar g;

    string str;
    while (getline(cin, str))
    {
        if (str[0] == 'q' || str[0] == 'Q')
            break;

        if (parse(str.c_str(), g, space_p).full)
        {
            cout << "parsing succeeded\n";
        }
        else
        {
            cout << "parsing failed\n";
        }
    }

    cout << "Bye... :-) \n\n";
    return 0;
}


// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2009 Benoit Jacob <jacob.benoit.1@gmail.com>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.

// This C++ file compiles to binary code that can be linked to by your C program,
// thanks to the extern "C" syntax used in the declarations in binary_library.h.

#include "binary_library.h"

#include <Eigen/Core>

using namespace Eigen;

/************************* pointer conversion methods **********************************************/

////// class MatrixXd //////

inline MatrixXd& c_to_eigen(C_MatrixXd* ptr)
{
  return *reinterpret_cast<MatrixXd*>(ptr);
}

inline const MatrixXd& c_to_eigen(const C_MatrixXd* ptr)
{
  return *reinterpret_cast<const MatrixXd*>(ptr);
}

inline C_MatrixXd* eigen_to_c(MatrixXd& ref)
{
  return reinterpret_cast<C_MatrixXd*>(&ref);
}

inline const C_MatrixXd* eigen_to_c(const MatrixXd& ref)
{
  return reinterpret_cast<const C_MatrixXd*>(&ref);
}

////// class Map<MatrixXd> //////

inline Map<MatrixXd>& c_to_eigen(C_Map_MatrixXd* ptr)
{
  return *reinterpret_cast<Map<MatrixXd>*>(ptr);
}

inline const Map<MatrixXd>& c_to_eigen(const C_Map_MatrixXd* ptr)
{
  return *reinterpret_cast<const Map<MatrixXd>*>(ptr);
}

inline C_Map_MatrixXd* eigen_to_c(Map<MatrixXd>& ref)
{
  return reinterpret_cast<C_Map_MatrixXd*>(&ref);
}

inline const C_Map_MatrixXd* eigen_to_c(const Map<MatrixXd>& ref)
{
  return reinterpret_cast<const C_Map_MatrixXd*>(&ref);
}

/************************* implementation of classes **********************************************/

////// class MatrixXd //////

C_MatrixXd* MatrixXd_new(int rows, int cols)
{
  return eigen_to_c(*new MatrixXd(rows,cols));
}

void MatrixXd_delete(C_MatrixXd *m)
{
  delete &c_to_eigen(m);
}

double* MatrixXd_data(C_MatrixXd *m)
{
  return c_to_eigen(m).data();
}

void MatrixXd_set_zero(C_MatrixXd *m)
{
  c_to_eigen(m).setZero();
}

void MatrixXd_resize(C_MatrixXd *m, int rows, int cols)
{
  c_to_eigen(m).resize(rows,cols);
}

void MatrixXd_copy(C_MatrixXd *dst, const C_MatrixXd *src)
{
  c_to_eigen(dst) = c_to_eigen(src);
}

void MatrixXd_copy_map(C_MatrixXd *dst, const C_Map_MatrixXd *src)
{
  c_to_eigen(dst) = c_to_eigen(src);
}

void MatrixXd_set_coeff(C_MatrixXd *m, int i, int j, double coeff)
{
  c_to_eigen(m)(i,j) = coeff;
}

double MatrixXd_get_coeff(const C_MatrixXd *m, int i, int j)
{
  return c_to_eigen(m)(i,j);
}

void MatrixXd_print(const C_MatrixXd *m)
{
  std::cout << c_to_eigen(m) << std::endl;
}

void MatrixXd_multiply(const C_MatrixXd *m1, const C_MatrixXd *m2, C_MatrixXd *result)
{
  c_to_eigen(result) = c_to_eigen(m1) * c_to_eigen(m2);
}

void MatrixXd_add(const C_MatrixXd *m1, const C_MatrixXd *m2, C_MatrixXd *result)
{
  c_to_eigen(result) = c_to_eigen(m1) + c_to_eigen(m2);
}

////// class Map_MatrixXd //////

C_Map_MatrixXd* Map_MatrixXd_new(double *array, int rows, int cols)
{
  return eigen_to_c(*new Map<MatrixXd>(array,rows,cols));
}

void Map_MatrixXd_delete(C_Map_MatrixXd *m)
{
  delete &c_to_eigen(m);
}

void Map_MatrixXd_set_zero(C_Map_MatrixXd *m)
{
  c_to_eigen(m).setZero();
}

void Map_MatrixXd_copy(C_Map_MatrixXd *dst, const C_Map_MatrixXd *src)
{
  c_to_eigen(dst) = c_to_eigen(src);
}

void Map_MatrixXd_copy_matrix(C_Map_MatrixXd *dst, const C_MatrixXd *src)
{
  c_to_eigen(dst) = c_to_eigen(src);
}

void Map_MatrixXd_set_coeff(C_Map_MatrixXd *m, int i, int j, double coeff)
{
  c_to_eigen(m)(i,j) = coeff;
}

double Map_MatrixXd_get_coeff(const C_Map_MatrixXd *m, int i, int j)
{
  return c_to_eigen(m)(i,j);
}

void Map_MatrixXd_print(const C_Map_MatrixXd *m)
{
  std::cout << c_to_eigen(m) << std::endl;
}

void Map_MatrixXd_multiply(const C_Map_MatrixXd *m1, const C_Map_MatrixXd *m2, C_Map_MatrixXd *result)
{
  c_to_eigen(result) = c_to_eigen(m1) * c_to_eigen(m2);
}

void Map_MatrixXd_add(const C_Map_MatrixXd *m1, const C_Map_MatrixXd *m2, C_Map_MatrixXd *result)
{
  c_to_eigen(result) = c_to_eigen(m1) + c_to_eigen(m2);
}


/* CirKit: A circuit toolkit
 * Copyright (C) 2009-2015  University of Bremen
 * Copyright (C) 2015-2017  EPFL
 *
 * 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 "aig_word.hpp"

#include <boost/assign/std/vector.hpp>
#include <boost/format.hpp>

namespace cirkit
{

using namespace boost::assign;

aig_word to_aig_word( const aig_function& f )
{
  return aig_word(1u, f);
}

const aig_function &to_aig_function( const aig_word& w )
{
  assert( w.size() == 1 );
  return w[0];
}

aig_word aig_create_wi( aig_graph& aig, const unsigned width, const std::string& name )
{
  assert( width > 0 && "Width must not be < 0" );
  aig_word w;
  for ( unsigned u = 0u; u < width; ++u )
  {
    const std::string n = (boost::format("%s[%d]") % name % u).str();
    w += ( aig_create_pi( aig, n ) );
  }
  return w;
}

void aig_create_wo( aig_graph& aig, const aig_word& w, const std::string& name )
{
  unsigned index = 0u;
  for ( auto& e : w )
  {
    const std::string n = (boost::format("%s[%d]") % name % index++).str();
    aig_create_po( aig, e, n);
  }
}

aig_word aig_create_bvbin( aig_graph& aig, const std::string& value )
{
  assert( value.size() > 0u );
  aig_word w( value.size() );
  for ( std::size_t s = 0u; s < value.size(); ++s )
  {
    assert( value[s] == '0' || value[s] == '1' );
    w[ value.size() - 1u - s] = aig_get_constant( aig, value[s] == '1' );
  }
  return w;
}

aig_function aig_create_equal( aig_graph& aig, const aig_word& left, const aig_word& right )
{
  assert( left.size() == right.size() );
  aig_function result = aig_get_constant( aig, false );
  for ( unsigned u = 0u; u < left.size(); ++u )
  {
    const aig_function current = aig_create_xor( aig, left[u], right[u] );
    result = aig_create_or( aig, current, result );
  }
  return !result;
}

aig_function aig_create_bvsgt( aig_graph& aig, const aig_word& left, const aig_word& right )
{
  assert( left.size() == right.size() );
  assert( left.size() > 0u );

  auto li = left.rbegin(), ri = right.rbegin(), end = left.rend();

  aig_function not_l = !*li;
  aig_function not_r = !*ri;
  aig_function result = aig_create_and( aig, not_l, *ri );
  aig_function equal = !aig_create_xor( aig, *li, *ri );

  for ( ++li, ++ri; li != end; ++li, ++ri )
  {
    not_r = !*ri;

    const auto now_great = aig_create_and( aig, *li, not_r );
    const auto now = aig_create_and( aig, equal, now_great );

    const auto now_equal = !aig_create_xor( aig, *li, *ri );
    equal = aig_create_and( aig, now_equal, equal );

    result = aig_create_or( aig, result, now );
  }

  return result;
}

aig_function aig_create_bvslt( aig_graph& aig, const aig_word& left, const aig_word& right )
{
  assert( left.size() == right.size() );
  assert( left.size() > 0u );

  auto li = left.rbegin(), ri = right.rbegin(), end = left.rend();

  aig_function not_l = !*li;
  aig_function result = aig_create_and( aig, *li, not_l );
  aig_function equal = !aig_create_xor( aig, *li, *ri );

  for ( ++li, ++ri; li != end; ++li, ++ri )
  {
    not_l = !*li;

    const auto now_less = aig_create_and( aig, not_l, *ri );
    const auto now = aig_create_and( aig, equal, now_less );

    const auto now_equal = !aig_create_xor( aig, *li, *ri );
    equal = aig_create_and( aig, now_equal, equal );

    result = aig_create_or( aig, result, now );
  }

  return result;
}

aig_function aig_create_bvsge( aig_graph& aig, const aig_word& left, const aig_word& right )
{
  assert( left.size() == right.size() );
  assert( left.size() > 0u );

  auto li = left.rbegin(), ri = right.rbegin(), end = left.rend();

  aig_function not_l = !*li;
  aig_function not_r = !*ri;
  aig_function great = aig_create_and( aig, not_l, *ri );
  aig_function equal = !aig_create_xor( aig, *li, *ri );
  aig_function result = great;

  for ( ++li, ++ri; li != end; ++li, ++ri )
  {
    not_r = !*ri;
    const auto now_great = aig_create_and( aig, *li, not_r );
    const auto now = aig_create_and( aig, equal, now_great );

    const auto now_equal = !aig_create_xor( aig, *li, *ri );
    equal = aig_create_and( aig, now_equal, equal );

    result = aig_create_or( aig, result, now );
  }
  return aig_create_or( aig, result, equal );
}

aig_function aig_create_bvsle( aig_graph& aig, const aig_word& left, const aig_word& right )
{
  assert( left.size() == right.size() );
  assert( left.size() > 0u );

  auto li = left.rbegin(), ri = right.rbegin(), end = left.rend();

  aig_function not_r = !*ri;
  aig_function less = aig_create_and( aig, *li, not_r );
  aig_function equal = !aig_create_xor( aig, *li, *ri );
  aig_function result = less;

  for ( ++li, ++ri; li != end; ++li, ++ri )
  {
    const aig_function not_l = !*li;
    const aig_function now_less = aig_create_and( aig, not_l, *ri );
    const aig_function now = aig_create_and( aig, equal, now_less );

    const aig_function now_equal = !aig_create_xor( aig, *li, *ri );
    equal = aig_create_and( aig, now_equal, equal );

    result = aig_create_or( aig, result, now );
  }
  return aig_create_or( aig, result, equal );
}

aig_function aig_create_bvule( aig_graph& aig, const aig_word& left, const aig_word& right )
{
  assert( left.size() == right.size() );
  assert( left.size() > 0 );

  auto li = left.rbegin(), ri = right.rbegin(), end = left.rend();
  aig_function not_l = !*li;
  aig_function less = aig_create_and( aig, not_l, *ri );
  aig_function equal = !aig_create_xor( aig, *li, *ri );
  aig_function ret = less;

  for (++li, ++ri; li != end; ++li, ++ri) {
    not_l = !*li;
    aig_function now_less = aig_create_and(aig, not_l, *ri);
    aig_function now = aig_create_and(aig, equal, now_less);
    aig_function now_equal = !aig_create_xor(aig, *li, *ri);
    equal = aig_create_and(aig, now_equal, equal);
    ret = aig_create_or(aig, ret, now);
  }
  ret = aig_create_or(aig, ret, equal);
  return ret;
}

aig_function aig_create_bvult( aig_graph& aig, const aig_word& left, const aig_word& right )
{
  assert( left.size() == right.size() );
  assert( left.size() > 0 );

  auto li = left.rbegin(), ri = right.rbegin(), end = left.rend();
  aig_function not_l = !*li;
  aig_function ret = aig_create_and(aig, not_l, *ri );
  aig_function equal = !aig_create_xor( aig, *li, *ri );

  for (++li, ++ri; li != end; ++li, ++ri) {
    not_l = !*li;
    aig_function now_less = aig_create_and(aig, not_l, *ri);
    aig_function now = aig_create_and(aig, equal, now_less);
    aig_function now_equal = !aig_create_xor(aig, *li, *ri);
    equal = aig_create_and(aig, now_equal, equal);
    ret = aig_create_or(aig, ret, now);
  }
  return ret;
}

aig_function aig_create_bvugt( aig_graph& aig, const aig_word& left, const aig_word& right )
{
  assert( left.size() == right.size() );
  assert( left.size() > 0 );

  auto li = left.rbegin(), ri = right.rbegin(), end = left.rend();
  aig_function not_r = !*ri;
  aig_function ret = aig_create_and(aig, *li, not_r );
  aig_function equal = !aig_create_xor( aig, *li, *ri );

  for (++li, ++ri; li != end; ++li, ++ri) {
    not_r = !*ri;
    aig_function now_great = aig_create_and(aig, *li, not_r);
    aig_function now = aig_create_and(aig, equal, now_great);
    aig_function now_equal = !aig_create_xor(aig, *li, *ri);
    equal = aig_create_and(aig, now_equal, equal);
    ret = aig_create_or(aig, ret, now);
  }
  return ret;
}

aig_function aig_create_bvuge( aig_graph& aig, const aig_word& left, const aig_word& right )
{
  assert( left.size() == right.size() );
  assert( left.size() > 0 );

  auto li = left.rbegin(), ri = right.rbegin(), end = left.rend();
  aig_function not_r = !*ri;
  aig_function great = aig_create_and(aig, *li, not_r );
  aig_function equal = !aig_create_xor( aig, *li, *ri );
  aig_function ret = great;

  for (++li, ++ri; li != end; ++li, ++ri) {
    not_r = !*ri;
    aig_function now_great = aig_create_and(aig, *li, not_r);
    aig_function now = aig_create_and(aig, equal, now_great);
    aig_function now_equal = !aig_create_xor(aig, *li, *ri);
    equal = aig_create_and(aig, now_equal, equal);
    ret = aig_create_or(aig, ret, now);
  }
  ret = aig_create_or(aig, ret, equal);
  return ret;
}

aig_word aig_create_bvand( aig_graph& aig, const aig_word& left, const aig_word& right )
{
  assert( left.size() == right.size() );
  aig_word result( left.size() );
  for ( unsigned u = 0; u < left.size(); ++u ) {
    result[u] = aig_create_and( aig, left[u], right[u] );
  }
  return result;
}

aig_word aig_create_bvor( aig_graph& aig, const aig_word& left, const aig_word& right )
{
  assert( left.size() == right.size() );
  aig_word result( left.size() );
  for ( unsigned u = 0; u < left.size(); ++u ) {
    result[u] = aig_create_or( aig, left[u], right[u] );
  }
  return result;
}

aig_word aig_create_bvxor( aig_graph& aig, const aig_word& left, const aig_word& right )
{
  assert( left.size() == right.size() );
  aig_word result( left.size() );
  for ( unsigned u = 0; u < left.size(); ++u ) {
    result[u] = aig_create_xor( aig, left[u], right[u] );
  }
  return result;
}

aig_word aig_create_bvadd( aig_graph& aig, const aig_word& left, const aig_word& right )
{
  assert( left.size() == right.size() );

  aig_word result( left.size() );
  aig_function carry = aig_get_constant( aig, false );

  aig_function xor1, or1, and1, and2;
  for ( unsigned u = 0u; u < left.size(); ++u )
  {
    xor1 = aig_create_xor( aig, left[u], right[u] );
    result[u] = aig_create_xor( aig, xor1, carry );

    // left & right | carry & ( left | right )
    and1 = aig_create_and( aig, left[u], right[u] );
    or1 = aig_create_or( aig, left[u], right[u] );
    and2 = aig_create_and( aig, carry, or1 );
    carry = aig_create_or( aig, and1, and2 );
  }

  return result;
}

aig_word shift_left( aig_graph& aig, const aig_word& w, unsigned value )
{
  aig_word result( w.size() );
  if ( value == 0u )
  {
    return w;
  }
  for ( unsigned i = 0u; i < w.size(); ++i )
  {
    if ( i < value )
    {
      result[ i ] = aig_get_constant( aig, false );
    }
    else
    {
      result[ i ] = w[ i - value ];
    }
  }
  return result;
}

aig_word aig_create_bvmul( aig_graph& aig, const aig_word& left, const aig_word& right )
{
  assert( left.size() == right.size() );

  aig_word result( left.size(), aig_get_constant( aig, false ) );

  aig_word tmp;
  for ( unsigned i = 0; i < result.size(); ++i )
  {
    tmp = aig_create_sext( aig, left.size() - 1u, to_aig_word( left[i] ) );
    tmp = aig_create_bvand( aig, right, tmp );
    tmp = shift_left( aig, tmp, i );

    result = aig_create_bvadd( aig, result, tmp );
  }
  return result;
}

aig_word aig_create_bvnot( aig_graph& aig, const aig_word& w )
{
  aig_word result( w.size() );
  for ( unsigned u = 0u; u < w.size(); ++u )
  {
    result[ u ] = !w[ u ];
  }
  return result;
}

aig_word aig_create_bvneg( aig_graph& aig, const aig_word& w )
{
  aig_word result( w.size(), aig_get_constant( aig, false ) );
  result.front() = aig_get_constant( aig, true );
  const aig_word tmp = aig_create_bvnot( aig, w );
  return aig_create_bvadd( aig, tmp, result );
}

aig_word aig_create_bvsub( aig_graph& aig, const aig_word& left, const aig_word& right )
{
  aig_word result( aig_create_bvneg( aig, right ) );
  return aig_create_bvadd( aig, left, result );
}

aig_word aig_create_sext( aig_graph& aig, const unsigned width, const aig_word& w )
{
  assert( w.size() > 0u );
  aig_word result( w.size() + width, w.back() );
  std::copy( w.begin(), w.end(), result.begin() );
  return result;
}

aig_word aig_create_zext( aig_graph& aig, const unsigned width, const aig_word& w )
{
  assert( w.size() > 0u );
  aig_word result( w.size() + width, aig_get_constant( aig, false ) );
  std::copy( w.begin(), w.end(), result.begin() );
  return result;
}

aig_word aig_create_ite( aig_graph& aig, const aig_function& cond, const aig_word& t, const aig_word& e )
{
  assert( t.size() == e.size() );
  const std::size_t size = t.size();
  aig_word result( size,  aig_get_constant( aig, false ) );
  for ( unsigned u = 0u; u < size; ++u )
  {
    result[ u ] = aig_create_ite( aig, cond, t[ u ], e[ u ] );
  }
  return result;
}

std::ostream& operator<<( std::ostream& os, const aig_word &w )
{
  os << "[ ";
  for ( const auto& it : w )
  {
    os << it << " ";
  }
  os << "]";
  return os;
}

}

// Local Variables:
// c-basic-offset: 2
// eval: (c-set-offset 'substatement-open 0)
// eval: (c-set-offset 'innamespace 0)
// End:


// Boost.Units - A C++ library for zero-overhead dimensional analysis and
// unit/quantity manipulation and conversion
//
// Copyright (C) 2003-2008 Matthias Christian Schabel
// Copyright (C) 2008 Steven Watanabe
//
// 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_UNITS_CODATA_PHYSICO_CHEMICAL_CONSTANTS_HPP
#define BOOST_UNITS_CODATA_PHYSICO_CHEMICAL_CONSTANTS_HPP

#include <boost/units/pow.hpp>
#include <boost/units/static_constant.hpp>

#include <boost/units/systems/detail/constants.hpp>
#include <boost/units/systems/si/amount.hpp>
#include <boost/units/systems/si/area.hpp>
#include <boost/units/systems/si/electric_charge.hpp>
#include <boost/units/systems/si/energy.hpp>
#include <boost/units/systems/si/frequency.hpp>
#include <boost/units/systems/si/mass.hpp>
#include <boost/units/systems/si/power.hpp>
#include <boost/units/systems/si/solid_angle.hpp>
#include <boost/units/systems/si/temperature.hpp>

#include <boost/units/systems/si/codata/typedefs.hpp>

/// \file
/// CODATA recommended values of fundamental physico-chemical constants
/// CODATA 2006 values as of 2007/03/30

namespace boost {

namespace units {

namespace si {

namespace constants {

namespace codata {

// PHYSICO-CHEMICAL
/// Avogadro constant
BOOST_UNITS_PHYSICAL_CONSTANT(N_A,quantity<inverse_amount>,6.02214179e23/mole,3.0e16/mole);
/// atomic mass constant
BOOST_UNITS_PHYSICAL_CONSTANT(m_u,quantity<mass>,1.660538782e-27*kilograms,8.3e-35*kilograms);
/// Faraday constant
BOOST_UNITS_PHYSICAL_CONSTANT(F,quantity<electric_charge_over_amount>,96485.3399*coulombs/mole,2.4e-3*coulombs/mole);
/// molar gas constant
BOOST_UNITS_PHYSICAL_CONSTANT(R,quantity<energy_over_temperature_amount>,8.314472*joules/kelvin/mole,1.5e-5*joules/kelvin/mole);
/// Boltzmann constant
BOOST_UNITS_PHYSICAL_CONSTANT(k_B,quantity<energy_over_temperature>,1.3806504e-23*joules/kelvin,2.4e-29*joules/kelvin);
/// Stefan-Boltzmann constant
BOOST_UNITS_PHYSICAL_CONSTANT(sigma_SB,quantity<power_over_area_temperature_4>,5.670400e-8*watts/square_meter/pow<4>(kelvin),4.0e-13*watts/square_meter/pow<4>(kelvin));
/// first radiation constant
BOOST_UNITS_PHYSICAL_CONSTANT(c_1,quantity<power_area>,3.74177118e-16*watt*square_meters,1.9e-23*watt*square_meters);
/// first radiation constant for spectral radiance
BOOST_UNITS_PHYSICAL_CONSTANT(c_1L,quantity<power_area_over_solid_angle>,1.191042759e-16*watt*square_meters/steradian,5.9e-24*watt*square_meters/steradian);
/// second radiation constant
BOOST_UNITS_PHYSICAL_CONSTANT(c_2,quantity<length_temperature>,1.4387752e-2*meter*kelvin,2.5e-8*meter*kelvin);
/// Wien displacement law constant : lambda_max T
BOOST_UNITS_PHYSICAL_CONSTANT(b,quantity<length_temperature>,2.8977685e-3*meter*kelvin,5.1e-9*meter*kelvin);
/// Wien displacement law constant : nu_max/T
BOOST_UNITS_PHYSICAL_CONSTANT(b_prime,quantity<frequency_over_temperature>,5.878933e10*hertz/kelvin,1.0e15*hertz/kelvin);

} // namespace codata

} // namespace constants

} // namespace si

} // namespace units

} // namespace boost

#endif // BOOST_UNITS_CODATA_PHYSICO_CHEMICAL_CONSTANTS_HPP


//
//=======================================================================
// Copyright 1997, 1998, 1999, 2000 University of Notre Dame.
// Authors: Andrew Lumsdaine, Lie-Quan Lee, Jeremy G. Siek
//
// This file is part of the Boost Graph Library
//
// You should have received a copy of the License Agreement for the
// Boost Graph Library along with the software; see the file LICENSE.
// If not, contact Office of Research, University of Notre Dame, Notre
// Dame, IN 46556.
//
// Permission to modify the code and to distribute modified code is
// granted, provided the text of this NOTICE is retained, a notice that
// the code was modified is included with the above COPYRIGHT NOTICE and
// with the COPYRIGHT NOTICE in the LICENSE file, and that the LICENSE
// file is distributed with the modified code.
//
// LICENSOR MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED.
// By way of example, but not limitation, Licensor MAKES NO
// REPRESENTATIONS OR WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY
// PARTICULAR PURPOSE OR THAT THE USE OF THE LICENSED SOFTWARE COMPONENTS
// OR DOCUMENTATION WILL NOT INFRINGE ANY PATENTS, COPYRIGHTS, TRADEMARKS
// OR OTHER RIGHTS.
//=======================================================================
//
#ifndef BOOST_GRAPH_CUTHILL_MCKEE_HPP
#define BOOST_GRAPH_CUTHILL_MCKEE_HPP

#include <boost/config.hpp>
#include <vector>
#include <queue>
#include <boost/pending/queue.hpp>
#include <boost/graph/graph_traits.hpp>
#include <boost/graph/breadth_first_search.hpp>
#include <boost/graph/properties.hpp>
#include <boost/pending/indirect_cmp.hpp>
#include <boost/property_map.hpp>

/*
  (Reverse) Cuthill-McKee Algorithm for matrix reordering
 */

namespace boost {

  namespace detail {

    // rcm_queue
    //
    // This is a custom queue type used in the
    // reverse_cuthill_mckee_ordering algorithm.
    // In addition to the normal queue operations, the
    // rcm_queue provides:
    //
    //   int eccentricity() const;
    //   value_type spouse() const;
    //
    template < class Vertex, class DegreeMap,
               class Container = std::deque<Vertex> >
    class rcm_queue : public std::queue<Vertex, Container> {
      typedef std::queue<Vertex> base;
    public:
      typedef typename base::value_type value_type;
      typedef typename base::size_type size_type;

      /* SGI queue has not had a contructor queue(const Container&) */
      inline rcm_queue(DegreeMap deg)
        : _size(0), Qsize(1), eccen(-1), degree(deg) { }

      inline void pop() {
        if ( !_size )
          Qsize = base::size();

        base::pop();
        if ( _size == Qsize-1 ) {
          _size = 0;
          ++eccen;
        } else
          ++_size;

      }

      inline value_type& front() {
        value_type& u =  base::front();
        if ( _size == 0 )
          w = u;
        else if (get(degree,u) < get(degree,w) )
          w = u;
        return u;
      }
      inline const value_type& front() const {
        const value_type& u =  base::front();
        if ( _size == 0 )
          w = u;
        else if (get(degree,u) < get(degree,w) )
          w = u;
        return u;
      }

      inline value_type& top() { return front(); }
      inline const value_type& top() const { return front(); }

      inline size_type size() const { return base::size(); }

      inline size_type eccentricity() const { return eccen; }
      inline value_type spouse() const { return w; }

    protected:
      size_type _size;
      size_type Qsize;
      int eccen;
      mutable value_type w;
      DegreeMap degree;
    };

  } // namespace detail

  // Compute Pseudo peripheral
  //
  // To compute an approximated peripheral for a given vertex.
  // Used in <tt>reverse_cuthill_mckee_ordering</tt> algorithm.
  //
  template <class Graph, class Vertex, class ColorMap, class DegreeMap>
  Vertex
  pseudo_peripheral_pair(Graph& G, const Vertex& u, int& ecc,
                         ColorMap color, DegreeMap degree)
  {
    typedef typename property_traits<ColorMap>::value_type ColorValue;
    typedef color_traits<ColorValue> Color;

    detail::rcm_queue<Vertex, DegreeMap> Q(degree);

    typename boost::graph_traits<Graph>::vertex_iterator ui, ui_end;
    for (tie(ui, ui_end) = vertices(G); ui != ui_end; ++ui)
      put(color, *ui, Color::white());
    breadth_first_search(G, u, buffer(Q).color_map(color));

    ecc = Q.eccentricity();
    return Q.spouse();
  }

  // Find a good starting node
  //
  // This is to find a good starting node for the
  // reverse_cuthill_mckee_ordering algorithm. "good" is in the sense
  // of the ordering generated by RCM.
  //
  template <class Graph, class Vertex, class Color, class Degree>
  Vertex find_starting_node(Graph& G, Vertex r, Color color, Degree degree)
  {
    Vertex x, y;
    int eccen_r, eccen_x;

    x = pseudo_peripheral_pair(G, r, eccen_r, color, degree);
    y = pseudo_peripheral_pair(G, x, eccen_x, color, degree);

    while (eccen_x > eccen_r) {
      r = x;
      eccen_r = eccen_x;
      x = y;
      y = pseudo_peripheral_pair(G, x, eccen_x, color, degree);
    }
    return x;
  }

  // Reverse Cuthill-McKee algorithm with a given starting Vertex.
  //
  // This algorithm requires user to provide a starting vertex to
  // compute RCM ordering.

  template <class Graph, class OutputIterator,
            class ColorMap, class DegreeMap>
  OutputIterator
  cuthill_mckee_ordering(Graph& g,
                         typename graph_traits<Graph>::vertex_descriptor s,
                         OutputIterator inverse_permutation,
                         ColorMap color, DegreeMap degree)
  {
    typedef typename property_traits<DegreeMap>::value_type DS;
    typedef typename property_traits<ColorMap>::value_type ColorValue;
    typedef color_traits<ColorValue> Color;
    typedef typename graph_traits<Graph>::vertex_descriptor Vertex;

    typename graph_traits<Graph>::vertex_iterator ui, ui_end;
    for (tie(ui, ui_end) = vertices(g); ui != ui_end; ++ui)
      put(color, *ui, Color::white());

    typedef indirect_cmp<DegreeMap, std::greater<DS> > Compare;
    Compare comp(degree);

    boost::queue<Vertex> bfs_queue;
    std::priority_queue<Vertex, std::vector<Vertex>, Compare>
      degree_queue(comp);
    Vertex u, v;

    // Like BFS, except the adjacent vertices are visited
    // in increasing order of degree.

    put(color, s, Color::gray());
    bfs_queue.push(s);
    while (! bfs_queue.empty()) {
      u = bfs_queue.top(); bfs_queue.pop();
      *inverse_permutation++ = u;
      typename graph_traits<Graph>::out_edge_iterator ei, ei_end;
      for (tie(ei, ei_end) = out_edges(u, g); ei != ei_end; ++ei) {
        v = target(*ei, g);
        if (get(color, v) == Color::white()) {
          put(color, v, Color::gray());
          degree_queue.push(v);
        }
      }
      while (!degree_queue.empty()) {
        v = degree_queue.top(); degree_queue.pop();
        bfs_queue.push(v);
      }
      put(color, u, Color::black());
    } // while
    return inverse_permutation;
  }

  template < class Graph, class OutputIterator,
             class Color, class Degree >
  inline OutputIterator
  cuthill_mckee_ordering(Graph& G, OutputIterator inverse_permutation,
                         Color color, Degree degree)
  {
    typedef typename boost::graph_traits<Graph>::vertex_descriptor Vertex;
    typedef typename boost::graph_traits<Graph>::vertex_iterator   VerIter;
    VerIter ri = vertices(G).first;
    Vertex r = *ri;

    //if G has several forests, how to let is cover all. ??

    Vertex s = find_starting_node(G, r, color, degree);
    return cuthill_mckee_ordering(G, s, inverse_permutation, color, degree);
  }

} // namespace boost

#endif // BOOST_GRAPH_CUTHILL_MCKEE_HPP


/*
 * stepper_details.cpp
 *
 * This example demonstrates some details about the steppers in odeint.
 *
 *
 * Copyright 2011-2012 Karsten Ahnert
 * Copyright 2012 Mario Mulansky
 * Copyright 2013 Pascal Germroth
 *
 * 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)
 */

#include <iostream>
#include <boost/array.hpp>
#include <boost/bind.hpp>
#include <boost/numeric/odeint.hpp>

using namespace std;
using namespace boost::numeric::odeint;

const size_t N = 3;

typedef boost::array< double , N > state_type;

//[ system_function_structure
void sys( const state_type & /*x*/ , state_type & /*dxdt*/ , const double /*t*/ )
{
    // ...
}
//]

void sys1( const state_type &/*x*/ , state_type &/*dxdt*/ , const double /*t*/ )
{
}

void sys2( const state_type &/*x*/ , state_type &/*dxdt*/ , const double /*t*/ )
{
}

//[ symplectic_stepper_detail_system_function
typedef boost::array< double , 1 > vector_type;

struct harm_osc_f1
{
    void operator()( const vector_type &p , vector_type &dqdt )
    {
        dqdt[0] = p[0];
    }
};

struct harm_osc_f2
{
    void operator()( const vector_type &q , vector_type &dpdt )
    {
        dpdt[0] = -q[0];
    }
};
//]

//[ symplectic_stepper_detail_system_class
struct harm_osc
{
    void f1( const vector_type &p , vector_type &dqdt ) const
    {
        dqdt[0] = p[0];
    }

    void f2( const vector_type &q , vector_type &dpdt ) const
    {
        dpdt[0] = -q[0];
    }
};
//]

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

    // Explicit stepper example
    {
        double t( 0.0 ) , dt( 0.1 );
        state_type in , out , dxdtin , inout;
        //[ explicit_stepper_detail_example
        runge_kutta4< state_type > rk;
        rk.do_step( sys1 , inout , t , dt );               // In-place transformation of inout
        rk.do_step( sys2 , inout , t , dt );               // call with different system: Ok
        rk.do_step( sys1 , in , t , out , dt );            // Out-of-place transformation
        rk.do_step( sys1 , inout , dxdtin , t , dt );      // In-place tranformation of inout
        rk.do_step( sys1 , in , dxdtin , t , out , dt );   // Out-of-place transformation
        //]
    }

    // FSAL stepper example
    {
        double t( 0.0 ) , dt( 0.1 );
        state_type in , in2 , in3 , out , dxdtin , dxdtout , inout , dxdtinout;
        //[ fsal_stepper_detail_example
        runge_kutta_dopri5< state_type > rk;
        rk.do_step( sys1 , in , t , out , dt );
        rk.do_step( sys2 , in , t , out , dt );         // DONT do this, sys1 is assumed

        rk.do_step( sys2 , in2 , t , out , dt );
        rk.do_step( sys2 , in3 , t , out , dt );        // DONT do this, in2 is assumed

        rk.do_step( sys1 , inout , dxdtinout , t , dt );
        rk.do_step( sys2 , inout , dxdtinout , t , dt );           // Ok, internal derivative is not used, dxdtinout is updated

        rk.do_step( sys1 , in , dxdtin , t , out , dxdtout , dt );
        rk.do_step( sys2 , in , dxdtin , t , out , dxdtout , dt ); // Ok, internal derivative is not used
        //]
    }

    // Symplectic harmonic oscillator example
    {
        double t( 0.0 ) , dt( 0.1 );
        //[ symplectic_stepper_detail_example
        pair< vector_type , vector_type > x;
        x.first[0] = 1.0; x.second[0] = 0.0;
        symplectic_rkn_sb3a_mclachlan< vector_type > rkn;
        rkn.do_step( make_pair( harm_osc_f1() , harm_osc_f2() ) , x , t , dt );
        //]

        //[ symplectic_stepper_detail_system_class_example
        harm_osc h;
        rkn.do_step( make_pair( boost::bind( &harm_osc::f1 , h , _1 , _2 ) , boost::bind( &harm_osc::f2 , h , _1 , _2 ) ) ,
                x , t , dt );
        //]
    }

    // Simplified harmonic oscillator example
    {
        double t = 0.0, dt = 0.1;
        //[ simplified_symplectic_stepper_example
        pair< vector_type , vector_type > x;
        x.first[0] = 1.0; x.second[0] = 0.0;
        symplectic_rkn_sb3a_mclachlan< vector_type > rkn;
        rkn.do_step( harm_osc_f1() , x , t , dt );
        //]

        vector_type q = {{ 1.0 }} , p = {{ 0.0 }};
        //[ symplectic_stepper_detail_ref_usage
        rkn.do_step( harm_osc_f1() , make_pair( boost::ref( q ) , boost::ref( p ) ) , t , dt );
        rkn.do_step( harm_osc_f1() , q , p , t , dt );
        rkn.do_step( make_pair( harm_osc_f1() , harm_osc_f2() ) , q , p , t , dt );
        //]
    }

    // adams_bashforth_moulton stepper example
    {
        double t = 0.0 , dt = 0.1;
        state_type inout;
        //[ multistep_detail_example
        adams_bashforth_moulton< 5 , state_type > abm;
        abm.initialize( sys , inout , t , dt );
        abm.do_step( sys , inout , t , dt );
        //]

        //[ multistep_detail_own_stepper_initialization
        abm.initialize( runge_kutta_fehlberg78< state_type >() , sys , inout , t , dt );
        //]
    }

    // dense output stepper examples
    {
        double t = 0.0 , dt = 0.1;
        state_type in;
        //[ dense_output_detail_example
        dense_output_runge_kutta< controlled_runge_kutta< runge_kutta_dopri5< state_type > > > dense;
        dense.initialize( in , t , dt );
        pair< double , double > times = dense.do_step( sys );
        (void)times;
        //]

        state_type inout;
        double t_start = 0.0 , t_end = 1.0;
        //[ dense_output_detail_generation1
        typedef boost::numeric::odeint::result_of::make_dense_output<
            runge_kutta_dopri5< state_type > >::type dense_stepper_type;
        dense_stepper_type dense2 = make_dense_output( 1.0e-6 , 1.0e-6 , runge_kutta_dopri5< state_type >() );
        (void)dense2;
        //]

        //[ dense_output_detail_generation2
        integrate_const( make_dense_output( 1.0e-6 , 1.0e-6 , runge_kutta_dopri5< state_type >() ) , sys , inout , t_start , t_end , dt );
        //]
    }

    return 0;
}


/***
八：变量模板与成员变量模板
变量模板:Variable Templates。C++14新标准中引入的(一般放在.h头文件中）
从感觉上，变量模板与函数模板有些类似，看起来象一个没有参数，但是有返回值的函数模板。
三种零初始化方式（T表示一个类型）
a)T();       //比如int temp1 = int();
b)T t = {};  //比如 int temp2 = {};
c)T {};      //比如int temp3 = int{};

（8.1）变量模板的特化
a)变量模板的全特化
变量模板特化的时候，并不需要正在特化的类型（double）与这个变量模板的类型（char）保持一致。
b)变量模板的偏特化
（8.2）默认模板参数
（8.3）非类型模板参数
（8.4）变量模板的另一种形式
（8.5）成员变量模板

九：别名模板与成员别名模板
别名模板 = "Alias Templates"，C++11新标准中引入的，引入的目的不但能简化书写，而且可以达到一些通过其他手段难以达到的效果。
***/

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

namespace _nmsp1
{
	//g_myvar的泛化版本
	template<typename T>
	T g_myvar{};    //写成T g_myvar =0;也不会出现语法错误，但该写法一般只适合数值类型
	                //{}是一种对变量的初始化方式，叫做零初始化。int = 0，指针=nullptr,bool = false
	//全特化
	template<>
	char g_myvar<double>{};

	//偏特化
	template<typename T>
	T g_myvar<T*>{120}; //要求T*必须依赖于T
	//T g_myvar<double*>{120};

}

namespace _nmsp2
{
	//g_myvar的泛化版本
	template<typename T = int>
	T g_myvar;
}

namespace _nmsp3
{
	template<typename T,int value>
	T g_myvar[value];
}

namespace _nmsp4
{
	template <typename T>
	struct B
	{
		const static T value = { 160 }; //const也可以写成constexpr,{}也可以不加
	};

	template <typename T>
	int g_myvar4 = B<T>::value; //注意g_myvar4是个变量模板

}

namespace _nmsp5
{
	template <typename T>
	class D
	{
	public:
		template <typename W>
		static W m_tpi; //静态成员变量模板声明
	};
	template <typename T>
	template <typename W>
	W D<T>::m_tpi = 5; //定义
}

namespace _nmsp6
{
	//别名模板
	template<typename T>
	using str_map_t = std::map<std::string, T>;

	template<typename T>
	class E
	{
		//成员别名模板
		template<typename T>
		using str_map_t = std::map<std::string, T>;

	public:
		str_map_t<int> map1;

	};
}

int main()
{

	//char* p{}; //nullptr,NULL
	//int q{}; //0
	/*char* p;
	int q;*/
	/*char* p = {};
	int q={};

	int temp1 = int();
	int temp2 = {};
	int temp3 = int{};
	*/

	//测试_nmsp1
	_nmsp1::g_myvar<float> = 15.6f;
	_nmsp1::g_myvar<int> = 13;
	cout << _nmsp1::g_myvar<float> << endl;
	cout << _nmsp1::g_myvar<int> << endl;

	_nmsp1::g_myvar<double> = '2';           //这个使用的就是刚刚特化版本的g_myvar变量模板

	cout << _nmsp1::g_myvar<double> << endl;
	printf("%d\n", _nmsp1::g_myvar<double>);

	cout << _nmsp1::g_myvar<int*> << endl;
	cout << _nmsp1::g_myvar<int> << endl;

	//测试_nmsp2
	_nmsp2::g_myvar<int> = 13;
	_nmsp2::g_myvar<> = 26;                 //尖括号不能省略,_nmsp2::g_myvar<>等价于_nmsp2::g_myvar<int>
	cout << _nmsp2::g_myvar<int> << endl;
	cout << _nmsp2::g_myvar<> << endl;

	//测试_nmsp3
	for (int i = 0; i < 15; ++i) //i = 0~14
	{
		_nmsp3::g_myvar<int,15>[i] = i;    //注意[]中的下标数字<=14，否则下标会越界。
									       //g_myvar<int,15>写法一出现就表示定义了g_myvar<int,15>[15]这个大小为15个元素的int类型数组。
	}
	for (int i = 0; i < 15; ++i)
	{
		cout << _nmsp3::g_myvar<int, 15>[i] << endl;
	}

	//测试_nmsp4
	cout << _nmsp4::g_myvar4<int> << endl; //160,g_myvar4<int>相当于B<int>::value
	_nmsp4::g_myvar4<int> = 152;
	cout << _nmsp4::g_myvar4<int> << endl; //152
	cout << _nmsp4::B<int>::value << endl; //160

	//测试_nmsp5
	cout << _nmsp5::D<float>::m_tpi<int> << endl;
	_nmsp5::D<float>::m_tpi<int> = 150;
	cout << _nmsp5::D<float>::m_tpi<int> << endl;

	//测试_nmsp6
	_nmsp6::str_map_t<int> map1;
	map1.insert({"first",1});
	map1.insert({"second",2 });

	_nmsp6::E<float> obja;
	obja.map1.insert({ "first",1 });
	obja.map1.insert({ "second",8 });

	return 0;
}