/*! \file parallelquicksort.cpp
    \brief スレッド並列化したクイックソートのパフォーマンスをチェックする

    Copyright © 2017-2020 @dc1394 All Rights Reserved.
    This software is released under the BSD 2-Clause License.
*/

#include <algorithm>                // for std::partition, std::sort
#include <array>                    // for std::array
#include <chrono>                   // for std::chrono
#include <cstdint>                  // for std::int32_t
#include <cstdio>                   // for std::fclose, std::fopen, std::fread, std::rewind
#include <execution>                // for std::execution
#include <fstream>                  // for std::ofstream
#include <iostream>                 // for std::cerr, std::cout, std::endl
#include <iterator>                 // for std::distance
#include <numeric>                  // for std::iota
#include <stack>                    // for std::stack
#include <stdexcept>                // for std::runtime_error
#include <thread>                   // for std::thread
#include <utility>                  // for std::pair
#include <vector>                   // for std::vector

#if defined(_MSC_VER) && defined(__llvm__)
    #include <string_view>          // for std::string_view_literals
    #include <system_error>         // for std::system_error

    #define WIN32_LEAN_AND_MEAN
    #include <Windows.h>            // for GetLastError, MultiByteToWideChar
#endif

#ifndef _MSC_VER
    #include <parallel/algorithm>   // for __gnu_parallel::sort
#else
    #include <ppl.h>                // for concurrency::parallel_sort, concurrency::parallel_buffered_sort
#endif

#include <pstl/algorithm>
#include <pstl/execution>           // for pstl::execution

#include <boost/assert.hpp>         // for boost::assert
#include <boost/format.hpp>         // for boost::format
#include <boost/process.hpp>        // for boost::process
#include <boost/thread.hpp>         // for boost::thread

#include <tbb/parallel_invoke.h>    // for tbb::parallel_invoke
#include <tbb/parallel_sort.h>      // for tbb::parallel_sort

namespace {
    //! A enumerated type
    /*!
        パフォーマンスをチェックする際の対象配列の種類を定義した列挙型
    */
    enum class Checktype : std::int32_t {
        // 完全にランダムなデータ
        RANDOM = 0,

        // あらかじめソートされたデータ
        SORT = 1,

        // 最初の1/4だけソートされたデータ
        QUARTERSORT = 2
    };

    //! A global variable (constant expression).
    /*!
        計測する回数
    */
    static auto constexpr CHECKLOOP = 10;

    //! A global variable (constant expression).
    /*!
        ソートする配列の要素数の最初の数
    */
    static auto constexpr N = 500;

    //! A global variable (constant).
    /*!
        実行するCPUの物理コア数
    */
    static std::int32_t const NUMPHYSICALCORE = boost::thread::physical_concurrency();

    //! A global variable (constant expression).
    /*!
        クイックソートをstd::threadで並列化する際の再帰数の上限
    */
    static auto constexpr STDTHREADRECMAX = 7;

    //! A global variable (constant).
    /*!
        クイックソートにおける閾値
    */
    static auto constexpr THRESHOLD = 500;

    //! A function.
    /*!
        並列化されたソート関数のパフォーマンスをチェックする
        \param checktype パフォーマンスをチェックする際の対象配列の種類
        \param ofs 出力用のファイルストリーム
		\return 成功したかどうか
    */
    bool check_performance(Checktype checktype, std::ofstream & ofs);

    //! A function.
    /*!
        引数で与えられたstd::functionの実行時間をファイルに出力する
        \param checktype パフォーマンスをチェックする際の対象配列の種類
        \param func 実行するstd::function
        \param n 配列のサイズ
        \param ofs 出力用のファイルストリーム
        \return funcの実行結果のstd::vector
    */
    std::vector<std::int32_t> elapsed_time(Checktype checktype, std::function<void(std::vector<std::int32_t> &)> const & func, std::int32_t n, std::ofstream & ofs);

#if defined(_MSC_VER) && defined(__llvm__)
    //! A function.
    /*!
        UTF-8エンコーディングの文字列をShift-JISエンコーディングの文字列に変換する
        \param u8str UTF-8エンコーディングの文字列
        \return Shift-JISエンコーディングの文字列
    */
    std::string myutf8tosjis(std::string_view const & u8str);
#endif

    template < class RandomIter >
    //! A template function.
    /*!
        指定された範囲の要素をクイックソートでソートする
        \param first 範囲の下限
        \param last 範囲の上限
    */
    void quick_sort(RandomIter first, RandomIter last)
    {
        using mypair = std::pair< RandomIter, RandomIter >;

        // 範囲の情報を格納するスタック
        std::stack< mypair, std::vector< mypair > > stack;

        // 範囲の上限と下限をスタックへ積む
        stack.push(std::make_pair(first, last - 1));

        // スタックが空になるまで繰り返す
        while (!stack.empty()) {
            // 範囲の情報をスタックから取り出す
            // C++17の構造化束縛を使う
            auto const [left, right] = stack.top();
            stack.pop();

            auto i = left;
            auto j = right;
            auto const pivot = (*left + *right) / 2;

            // 左右から進めてきたiとjがぶつかるまでループ
            while (i <= j) {
                // 基準値以上の値が見つかるまで右方向へ進めていく
                while (*i < pivot) {
                    ++i;
                }

                // 基準値以下の値が見つかるまで左方向へ進めていく
                while (*j > pivot) {
                    --j;
                }

                // 左右から進めてきたiとjがぶつかったら
                if (i <= j) {
                    // 基準値位置よりも左側にあり、基準値よりも大きい値と、
                    // 基準値位置よりも右側にあり、基準値よりも小さい値の
                    // 位置関係を交換する。
                    std::iter_swap(i, j);

                    // 次回に備えて、注目点をずらす
                    ++i;

                    // 境界チェック
                    if (j != first) {
                        // 次回に備えて、注目点をずらす
                        --j;
                    }
                }
            }

            // 左右の配列のうち、要素数が少ない方を先に処理する
            // 後で処理する側は、その範囲をスタックへ積んでおく
            if (left < j) {
                // 右側の配列を次のソート処理の対象にする
                stack.push(std::make_pair(left, j));
            }

            if (i < right) {
                // 左側の配列を次のソート処理の対象にする
                stack.push(std::make_pair(i, right));
            }
        }
    }

    template < class RandomIter >
    //! A template function.
    /*!
        指定された範囲の要素をクイックソートでソートする（oneTBBで並列化）
        \param first 範囲の下限
        \param last 範囲の上限
        \param reci 現在の再帰の深さ
    */
    void quick_sort_onetbb(RandomIter first, RandomIter last, std::int32_t reci)
    {
        // 部分ソートの要素数
        auto const num = std::distance(first, last);

        if (num <= 1) {
            // 部分ソートの要素数が1個以下なら何もすることはない
            return;
        }

        // 再帰の深さ + 1
        reci++;

        // 部分ソートが小さくなりすぎるとシリアル実行のほうが効率が良くなるため
        // 部分ソートの要素数が閾値以上の時だけ再帰させる
        // かつ、現在の再帰の深さが物理コア数以下のときだけ再帰させる
        if (num >= THRESHOLD && reci <= NUMPHYSICALCORE) {
            // 交点まで左右から入れ替えして交点を探す
            auto const middle = std::partition(first + 1, last, [first](auto n) { return n < *first; });

            // 交点 - 1の位置
            auto const mid = middle - 1;

            // 交点を移動
            std::iter_swap(first, mid);

            // 二つのラムダ式を別スレッドで実行
            tbb::parallel_invoke(
                // 下部をソート
                [first, mid, reci]() { quick_sort_onetbb(first, mid, reci); },
                // 上部をソート
                [middle, last, reci]() { quick_sort_onetbb(middle, last, reci); });
        }
        else {
            // 再帰なしのクイックソートの関数を呼び出す
            quick_sort(first, last);
        }
    }

    template < class RandomIter >
    //! A template function.
    /*!
        指定された範囲の要素をクイックソートでソートする（oneTBBで並列化）
        \param first 範囲の下限
        \param last 範囲の上限
    */
    inline void quick_sort_onetbb(RandomIter first, RandomIter last)
    {
        // 再帰ありの並列クイックソートの関数を呼び出す
        quick_sort_onetbb(first, last, 0);
    }

#if _OPENMP >= 200805
    template < class RandomIter >
    //! A template function.
    /*!
        指定された範囲の要素をクイックソートでソートする（OpenMPで並列化）
        \param first 範囲の下限
        \param last 範囲の上限
        \param reci 現在の再帰の深さ
    */
    void quick_sort_openmp(RandomIter first, RandomIter last, std::int32_t reci)
    {
        // 部分ソートの要素数
        auto const num = std::distance(first, last);

        if (num <= 1) {
            // 部分ソートの要素数が1個以下なら何もすることはない
            return;
        }

        // 再帰の深さ + 1
        reci++;

        // 部分ソートが小さくなりすぎるとシリアル実行のほうが効率が良くなるため
        // 部分ソートの要素数が閾値以上の時だけ再帰させる
        // かつ、現在の再帰の深さが物理コア数以下のときだけ再帰させる
        if (num >= THRESHOLD && reci <= NUMPHYSICALCORE) {
            // 交点まで左右から入れ替えして交点を探す
            auto const middle = std::partition(first + 1, last, [first](auto n) { return n < *first; });

            // 交点 - 1の位置
            auto const mid = middle - 1;

            // 交点を移動
            std::iter_swap(first, mid);

            // 次の関数をタスクとして実行
#pragma omp task
            // 下部をソート
            quick_sort_openmp(first, mid, reci);

            // 次の関数をタスクとして実行
#pragma omp task
            // 上部をソート
            quick_sort_openmp(middle, last, reci);

            // 二つのタスクの終了を待機
#pragma omp taskwait
        }
        else {
            // 再帰なしのクイックソートの関数を呼び出す
            quick_sort(first, last);
        }
    }

    template < class RandomIter >
    //! A template function.
    /*!
        指定された範囲の要素をクイックソートでソートする（OpenMPで並列化）
        \param first 範囲の下限
        \param last 範囲の上限
    */
    inline void quick_sort_openmp(RandomIter first, RandomIter last)
    {
#pragma omp parallel    // OpenMP並列領域の始まり
#pragma omp single      // task句はsingle領域で実行
        // 再帰ありの並列クイックソートを呼び出す
        quick_sort_openmp(first, last, 0);
    }
#endif

    template < class RandomIter >
    //! A template function.
    /*!
        指定された範囲の要素をクイックソートでソートする（std::threadで並列化）
        \param first 範囲の下限
        \param last 範囲の上限
        \param reci 現在の再帰の深さ
    */
    void quick_sort_thread(RandomIter first, RandomIter last, std::int32_t reci)
    {
        // 部分ソートの要素数
        auto const num = std::distance(first, last);

        if (num <= 1) {
            // 部分ソートの要素数が1個以下なら何もすることはない
            return;
        }

        // 再帰の深さ + 1
        reci++;

        // 部分ソートが小さくなりすぎるとシリアル実行のほうが効率が良くなるため
        // 部分ソートの要素数が閾値以上の時だけ再帰させる
        // かつ、現在の再帰の深さがSTDTHREADRECMAX( = 7)以下のときだけ再帰させる
        if (num >= THRESHOLD && reci <= STDTHREADRECMAX) {
            // 交点まで左右から入れ替えして交点を探す
            auto const middle = std::partition(first + 1, last, [first](auto n) { return n < *first; });

            // 交点 - 1の位置
            auto const mid = middle - 1;

            // 交点を移動
            std::iter_swap(first, mid);

            // 下部をソート（別スレッドで実行）
            auto th1 = std::thread([first, mid, reci]() { quick_sort_thread(first, mid, reci); });

            // 上部をソート（別スレッドで実行）
            auto th2 = std::thread([middle, last, reci]() { quick_sort_thread(middle, last, reci); });

            // 二つのスレッドの終了を待機
            th1.join();
            th2.join();
        }
        else {
            // 再帰なしのクイックソートの関数を呼び出す
            quick_sort(first, last);
        }
    }

    template < class RandomIter >
    //! A template function.
    /*!
        指定された範囲の要素をクイックソートでソートする（std::threadで並列化）
        \param first 範囲の下限
        \param last 範囲の上限
    */
    inline void quick_sort_thread(RandomIter first, RandomIter last)
    {
        // 再帰ありの並列クイックソートの関数を呼び出す
        quick_sort_thread(first, last, 0);
    }

#ifdef DEBUG_CHECK_RECUL
    //! A function.
    /*!
        再帰数の上限でパフォーマンスがどう変化するかチェックする
        \param ofs 出力用のファイルストリーム
    */
    void check_performance_recul(std::ofstream & ofs);

    //! A function.
    /*!
        引数で与えられたstd::functionの実行時間をファイルに出力する
        \param func 実行するstd::function
        \param n 配列のサイズ
        \param ofs 出力用のファイルストリーム
    */
    void elapsed_time_recul(std::function<void(std::vector<std::int32_t> &)> const & func, std::int32_t n, std::ofstream & ofs);

#if _OPENMP >= 200805
    template < class RandomIter >
    //! A template function.
    /*!
        指定された範囲の要素をクイックソートでソートする（OpenMPで並列化）
        \param first 範囲の下限
        \param last 範囲の上限
        \param reci 現在の再帰の深さ
        \param recul 再帰数の上限
    */
    void quick_sort_openmp_recul(RandomIter first, RandomIter last, std::int32_t reci, std::int32_t recul)
    {
        // 部分ソートの要素数
        auto const num = std::distance(first, last);

        if (num <= 1) {
            // 部分ソートの要素数が1個以下なら何もすることはない
            return;
        }

        // 再帰の深さ + 1
        reci++;

        // 部分ソートが小さくなりすぎるとシリアル実行のほうが効率が良くなるため
        // 部分ソートの要素数が閾値以上の時だけ再帰させる
        // かつ、現在の再帰の深さが物理コア数以下のときだけ再帰させる
        if (num >= THRESHOLD && reci <= recul) {
            // 交点まで左右から入れ替えして交点を探す
            auto const middle = std::partition(first + 1, last, [first](auto n) { return n < *first; });

            // 交点 - 1の位置
            auto const mid = middle - 1;

            // 交点を移動
            std::iter_swap(first, mid);

            // 次の関数をタスクとして実行
#pragma omp task
            // 下部をソート
            quick_sort_openmp_recul(first, mid, reci, recul);

            // 次の関数をタスクとして実行
#pragma omp task
            // 上部をソート
            quick_sort_openmp_recul(middle, last, reci, recul);

            // 二つのタスクの終了を待機
#pragma omp taskwait
        }
        else {
            // 再帰なしのクイックソートの関数を呼び出す
            quick_sort(first, last);
        }
    }

    template < class RandomIter >
    //! A template function.
    /*!
        指定された範囲の要素をクイックソートでソートする（OpenMPで並列化）
        \param first 範囲の下限
        \param last 範囲の上限
        \param recul 再帰数の上限
    */
    inline void quick_sort_openmp_recul(RandomIter first, RandomIter last, std::int32_t recul)
    {
#pragma omp parallel    // OpenMP並列領域の始まり
#pragma omp single      // task句はsingle領域で実行
        // 再帰ありの並列クイックソートを呼び出す
        quick_sort_openmp_recul(first, last, 0, recul);
    }
#endif

    template < class RandomIter >
    //! A template function.
    /*!
        指定された範囲の要素をクイックソートでソートする（oneTBBで並列化）
        \param first 範囲の下限
        \param last 範囲の上限
        \param reci 現在の再帰の深さ
        \param recul 再帰数の上限
    */
    void quick_sort_tbb_recul(RandomIter first, RandomIter last, std::int32_t reci, std::int32_t recul)
    {
        // 部分ソートの要素数
        auto const num = std::distance(first, last);

        if (num <= 1) {
            // 部分ソートの要素数が1個以下なら何もすることはない
            return;
        }

        // 再帰の深さ + 1
        reci++;

        // 部分ソートが小さくなりすぎるとシリアル実行のほうが効率が良くなるため
        // 部分ソートの要素数が閾値以上の時だけ再帰させる
        // かつ、現在の再帰の深さが物理コア数以下のときだけ再帰させる
        if (num >= THRESHOLD && reci <= recul) {
            // 交点まで左右から入れ替えして交点を探す
            auto const middle = std::partition(first + 1, last, [first](auto n) { return n < *first; });

            // 交点 - 1の位置
            auto const mid = middle - 1;

            // 交点を移動
            std::iter_swap(first, mid);

            // 二つのラムダ式を別スレッドで実行
            tbb::parallel_invoke(
                // 下部をソート
                [first, mid, reci, recul]() { quick_sort_tbb_recul(first, mid, reci, recul); },
                // 上部をソート
                [middle, last, reci, recul]() { quick_sort_tbb_recul(middle, last, reci, recul); });
        }
        else {
            // 再帰なしのクイックソートの関数を呼び出す
            quick_sort(first, last);
        }
    }

    template < class RandomIter >
    //! A template function.
    /*!
        指定された範囲の要素をクイックソートでソートする（oneTBBで並列化）
        \param first 範囲の下限
        \param last 範囲の上限
        \param recul 再帰数の上限
    */
    inline void quick_sort_tbb_recul(RandomIter first, RandomIter last, std::int32_t recul)
    {
        // 再帰ありの並列クイックソートの関数を呼び出す
        quick_sort_tbb_recul(first, last, 0, recul);
    }

    template < class RandomIter >
    //! A template function.
    /*!
        指定された範囲の要素をクイックソートでソートする（std::threadで並列化）
        \param first 範囲の下限
        \param last 範囲の上限
        \param reci 現在の再帰の深さ
        \param recul 再帰数の上限
    */
    void quick_sort_thread_recul(RandomIter first, RandomIter last, std::int32_t reci, std::int32_t recul)
    {
        // 部分ソートの要素数
        auto const num = std::distance(first, last);

        if (num <= 1) {
            // 部分ソートの要素数が1個以下なら何もすることはない
            return;
        }

        // 再帰の深さ + 1
        reci++;

        // 部分ソートが小さくなりすぎるとシリアル実行のほうが効率が良くなるため
        // 部分ソートの要素数が閾値以上の時だけ再帰させる
        // かつ、現在の再帰の深さが物理コア数以下のときだけ再帰させる
        if (num >= THRESHOLD && reci <= recul) {
            // 交点まで左右から入れ替えして交点を探す
            auto const middle = std::partition(first + 1, last, [first](auto n) { return n < *first; });

            // 交点 - 1の位置
            auto const mid = middle - 1;

            // 交点を移動
            std::iter_swap(first, mid);

            // 下部をソート（別スレッドで実行）
            auto th1 = std::thread([first, mid, reci, recul]() { quick_sort_thread_recul(first, mid, reci, recul); });

            // 上部をソート（別スレッドで実行）
            auto th2 = std::thread([middle, last, reci, recul]() { quick_sort_thread_recul(middle, last, reci, recul); });

            // 二つのスレッドの終了を待機
            th1.join();
            th2.join();
        }
        else {
            // 再帰なしのクイックソートの関数を呼び出す
            quick_sort(first, last);
        }
    }

    template < class RandomIter >
    //! A template function.
    /*!
        指定された範囲の要素をクイックソートでソートする（std::threadで並列化）
        \param first 範囲の下限
        \param last 範囲の上限
    */
    inline void quick_sort_thread_recul(RandomIter first, RandomIter last, std::int32_t recul)
    {
        // 再帰ありの並列クイックソートの関数を呼び出す
        quick_sort_thread_recul(first, last, 0, recul);
    }
#endif

#ifdef DEBUG
    //! A template function.
    /*!
        与えられた二つのstd::vectorのすべての要素が同じかどうかチェックする
        \param v1 一つ目のstd::vector
        \param v2 二つめのstd::vector
        \return 与えられた二つのstd::vectorのすべての要素が同じならtrue、そうでないならfalse
    */
    bool vec_check(std::vector<std::int32_t> const & v1, std::vector<std::int32_t> const & v2);
#endif
}

int main()
{
#if defined(_MSC_VER) && defined(__llvm__)
    using namespace std::string_view_literals;

    std::cout << myutf8tosjis(u8R"(物理コア数: )"sv) << boost::thread::physical_concurrency();
    std::cout << myutf8tosjis(u8R"(, 論理コア数: )"sv) << boost::thread::hardware_concurrency() << std::endl;

#ifdef DEBUG_CHECK_RECUL
    std::ofstream ofsrec(myutf8tosjis(u8R"(完全にシャッフルされたデータ_再帰数チェック.csv)"sv));
    check_performance_recul(ofsrec);
#else
    std::ofstream ofsrandom(myutf8tosjis(u8R"(完全にシャッフルされたデータ.csv)"sv));
    std::ofstream ofssort(myutf8tosjis(u8R"(あらかじめソートされたデータ.csv)"sv));
    std::ofstream ofsquartersort(myutf8tosjis(u8R"(最初の1_4だけソートされたデータ.csv)"sv));

    std::cout << myutf8tosjis(u8R"(完全にシャッフルされたデータを計測中...)"sv) << '\n';
    if (!check_performance(Checktype::RANDOM, ofsrandom)) {
        return -1;
    }

    std::cout << '\n' << myutf8tosjis(u8R"(あらかじめソートされたデータを計測中...)"sv) << '\n';
    if (!check_performance(Checktype::SORT, ofssort)) {
        return -1;
    }

    std::cout << '\n' << myutf8tosjis(u8R"(最初の1/4だけソートされたデータを計測中...)"sv) << '\n';
    if (!check_performance(Checktype::QUARTERSORT, ofsquartersort)) {
        return -1;
    }
#endif

#else
    std::cout << "物理コア数: " << boost::thread::physical_concurrency();
    std::cout << ", 論理コア数: " << boost::thread::hardware_concurrency() << std::endl;

#ifdef DEBUG_CHECK_RECUL
    std::ofstream ofsrec("完全にシャッフルされたデータ_再帰数チェック.csv");
    check_performance_recul(ofsrec);
#else
    std::ofstream ofsrandom("完全にシャッフルされたデータ.csv");
    std::ofstream ofssort("あらかじめソートされたデータ.csv");
    std::ofstream ofsquartersort("最初の1_4だけソートされたデータ.csv");

    std::cout << "完全にシャッフルされたデータを計測中...\n";
    if (!check_performance(Checktype::RANDOM, ofsrandom)) {
        return -1;
    }

    std::cout << "\nあらかじめソートされたデータを計測中...\n";
    if (!check_performance(Checktype::SORT, ofssort)) {
        return -1;
    }

    std::cout << "\n最初の1/4だけソートされたデータを計測中...\n";
    if (!check_performance(Checktype::QUARTERSORT, ofsquartersort)) {
        return -1;
    }
#endif
#endif

    return 0;
}

namespace {
    bool check_performance(Checktype checktype, std::ofstream & ofs)
    {
#ifndef _MSC_VER
        std::array< std::uint8_t, 3 > const bom = { 0xEF, 0xBB, 0xBF };
        ofs.write(reinterpret_cast<char const *>(bom.data()), sizeof(bom));
#endif

#if defined(_MSC_VER) && _OPENMP < 200805
        ofs << "配列の要素数,std::sort,クイックソート,std::thread,oneTBB,concurrency::parallel_sort,concurrency::parallel_buffered_sort,tbb::parallel_sort,std::sort (MSVC内蔵のParallelism TS),std::sort (Parallel STLのParallelism TS)\n";
#elif defined(_MSC_VER) && defined(__llvm__)
        using namespace std::string_view_literals;

        ofs << myutf8tosjis(u8R"(配列の要素数,std::sort,クイックソート,std::thread,OpenMP,oneTBB,concurrency::parallel_sort,concurrency::parallel_buffered_sort,tbb::parallel_sort,std::sort (MSVC内蔵のParallelism TS),std::sort (Parallel STLのParallelism TS))"sv) << '\n';
#else
        ofs << u8R"(配列の要素数,std::sort,クイックソート,std::thread,OpenMP,oneTBB,__gnu_parallel::sort,tbb::parallel_sort,std::sort (Parallelism TS),std::sort (Parallel STLのParallelism TS))" << '\n';
#endif

        auto issuccess = true;

        auto n = N;
        for (auto i = 0; i < 6; i++) {
            for (auto j = 0; j < 2; j++) {
#if defined(_MSC_VER) && defined(__llvm__)
                std::cout << n << myutf8tosjis(u8R"(個を計測中...)"sv) << '\n';
#else
                std::cout << n << "個を計測中...\n";
#endif
                std::vector<std::int32_t> vec(n);
                std::iota(vec.begin(), vec.end(), 1);

                std::array< std::vector<std::int32_t>, 10 > vecar;

                ofs << n << ',';

                try {
                    vecar[0] = elapsed_time(checktype, [](auto && vec) { std::sort(vec.begin(), vec.end()); }, n, ofs);
                    vecar[1] = elapsed_time(checktype, [](auto && vec) { quick_sort(vec.begin(), vec.end()); }, n, ofs);
                    vecar[2] = elapsed_time(checktype, [](auto && vec) { quick_sort_thread(vec.begin(), vec.end()); }, n, ofs);

#if _OPENMP >= 200805
                    vecar[3] = elapsed_time(checktype, [](auto && vec) { quick_sort_openmp(vec.begin(), vec.end()); }, n, ofs);
#endif
                    vecar[4] = elapsed_time(checktype, [](auto && vec) { quick_sort_onetbb(vec.begin(), vec.end()); }, n, ofs);

#ifndef _MSC_VER
                    vecar[5] = elapsed_time(checktype, [](auto && vec) { __gnu_parallel::sort(vec.begin(), vec.end()); }, n, ofs);
#else
                    vecar[5] = elapsed_time(checktype, [](auto && vec) { concurrency::parallel_sort(vec.begin(), vec.end()); }, n, ofs);

                    vecar[6] = elapsed_time(checktype, [](auto && vec) { concurrency::parallel_buffered_sort(vec.begin(), vec.end()); }, n, ofs);
#endif

                    vecar[7] = elapsed_time(checktype, [](auto && vec) { tbb::parallel_sort(vec); }, n, ofs);

                    vecar[8] = elapsed_time(checktype, [](auto && vec) { std::sort(std::execution::par, vec.begin(), vec.end()); }, n, ofs);

#ifdef _MSC_VER
                    vecar[9] = elapsed_time(checktype, [](auto && vec) { std::sort(pstl::execution::par, vec.begin(), vec.end()); }, n, ofs);
#else
                    vecar[9] = elapsed_time(checktype, [](auto && vec) { std::sort(__pstl::execution::par, vec.begin(), vec.end()); }, n, ofs);
#endif

                }
                catch (std::runtime_error const & e) {
                    std::cerr << e.what() << std::endl;
                    return false;
                }

                ofs << std::endl;

#ifdef DEBUG
                for (auto k = 0U; k < vecar.size(); k++) {
#if _OPENMP < 200805
                    if (k == 3) {
                        continue;
                    }
#endif

                    if (!k || k == 6 || k == 7 || k == 8) {
                        continue;
                    }

                    if (static_cast<std::int32_t>(vecar[k].size()) != n) {
                        issuccess = false;
                        continue;
                    }

                    if (!vec_check(vec, vecar[k])) {
                        std::cerr << "Error! vecar[" << k << ']' << std::endl;
                        issuccess = false;
                    }
                }
#endif
                if (!j) {
                    n *= 2;
                }
            }

            n *= 5;
        }

        return issuccess;
    }

    std::vector<std::int32_t> elapsed_time(Checktype checktype, std::function<void(std::vector<std::int32_t> &)> const & func, std::int32_t n, std::ofstream & ofs)
    {
        using namespace std::chrono;

        std::vector<std::int32_t> vec(n);
        std::unique_ptr< FILE, decltype(&std::fclose) > fp(nullptr, fclose);

        auto const program_name = "makequicksortdata";

        switch (checktype) {
        case Checktype::RANDOM:
            {
                auto const filename = (boost::format("sortdata_%d_rand.dat") % n).str();
                fp = std::unique_ptr< FILE, decltype(&std::fclose) >(std::fopen(filename.c_str(), "rb"), std::fclose);
                if (!fp) {
                    boost::process::child(program_name + (boost::format(" 0 %d") % n).str()).wait();
                    fp = std::unique_ptr< FILE, decltype(&std::fclose) >(std::fopen(filename.c_str(), "rb"), std::fclose);
                }
            }
            break;

        case Checktype::SORT:
            {
                auto const filename = (boost::format("sortdata_%d_already.dat") % n).str();
                fp = std::unique_ptr< FILE, decltype(&std::fclose) >(std::fopen(filename.c_str(), "rb"), std::fclose);
                if (!fp) {
                    boost::process::child(program_name + (boost::format(" 1 %d") % n).str()).wait();
                    fp = std::unique_ptr< FILE, decltype(&std::fclose) >(std::fopen(filename.c_str(), "rb"), std::fclose);
                }
            }
            break;

        case Checktype::QUARTERSORT:
            {
                auto const filename = (boost::format("sortdata_%d_quartersort.dat") % n).str();
                fp = std::unique_ptr< FILE, decltype(&std::fclose) >(std::fopen(filename.c_str(), "rb"), std::fclose);
                if (!fp) {
                    boost::process::child(program_name + (boost::format(" 2 %d") % n).str()).wait();
                    fp = std::unique_ptr< FILE, decltype(&std::fclose) >(std::fopen(filename.c_str(), "rb"), std::fclose);
                }
            }
            break;

        default:
            BOOST_ASSERT(!"switch文のdefaultに来てしまった！");
            break;
        }

        auto const readsize = vec.size();
        if (readsize != std::fread(vec.data(), sizeof(std::int32_t), readsize, fp.get())) {
            throw std::runtime_error("std::freadに失敗");
        }

        auto elapsed_time = 0.0;

        for (auto i = 1; i <= CHECKLOOP; i++) {
            auto const beg = high_resolution_clock::now();
            func(vec);
            auto const end = high_resolution_clock::now();

            elapsed_time += (duration_cast<duration<double>>(end - beg)).count();

            if (i != CHECKLOOP) {
                std::rewind(fp.get());
                if (readsize != std::fread(vec.data(), sizeof(std::int32_t), readsize, fp.get())) {
                    throw std::runtime_error("std::freadに失敗");
                }
            }
        }

        ofs << boost::format("%.10f") % (elapsed_time / static_cast<double>(CHECKLOOP)) << ',';

        return vec;
    }

#ifdef DEBUG_CHECK_RECUL
    void check_performance_recul(std::ofstream& ofs)
    {
        using namespace std::string_view_literals;

#if _OPENMP < 200805
        ofs << "再帰数,std::thread,oneTBB\n";
#else
        ofs << myutf8tosjis(u8R"(再帰数,std::thread,OpenMP,oneTBB)"sv) << '\n';
#endif

        for (auto recul = 0U; recul <= boost::thread::hardware_concurrency(); recul++) {
#if defined(_MSC_VER) && defined(__llvm__)
            std::cout << myutf8tosjis(u8R"(再帰数: )"sv) << recul << myutf8tosjis(u8R"(を計測中...)"sv) << '\n';
#else
            std::cout << "再帰数: " << recul << "を計測中...\n";
#endif

            ofs << recul << ',';
            elapsed_time_recul([recul](auto && vec) { quick_sort_thread_recul(vec.begin(), vec.end(), recul); }, 5000000, ofs);
#if _OPENMP >= 200805
            elapsed_time_recul([recul](auto && vec) { quick_sort_openmp_recul(vec.begin(), vec.end(), recul); }, 5000000, ofs);
#endif
            elapsed_time_recul([recul](auto && vec) { quick_sort_tbb_recul(vec.begin(), vec.end(), recul); }, 5000000, ofs);
            ofs << std::endl;
        }
    }

    void elapsed_time_recul(std::function<void(std::vector<std::int32_t>&)> const& func, std::int32_t n, std::ofstream& ofs)
    {
        using namespace std::chrono;

        std::vector<std::int32_t> vec(n);
        std::unique_ptr< FILE, decltype(&std::fclose) > fp(nullptr, fclose);

        auto const program_name = "makequicksortdata";

        auto const filename = (boost::format("sortdata_%d_rand.dat") % n).str();
        fp = std::unique_ptr< FILE, decltype(&std::fclose) >(std::fopen(filename.c_str(), "rb"), std::fclose);
        if (!fp) {
            boost::process::child(program_name + (boost::format(" 0 %d") % n).str()).wait();
            fp = std::unique_ptr< FILE, decltype(&std::fclose) >(std::fopen(filename.c_str(), "rb"), std::fclose);
        }

        auto const readsize = vec.size();
        if (readsize != std::fread(vec.data(), sizeof(std::int32_t), readsize, fp.get())) {
            throw std::runtime_error("std::freadに失敗");
        }

        auto elapsed_time = 0.0;

        for (auto i = 1; i <= CHECKLOOP; i++) {
            auto const beg = high_resolution_clock::now();
            func(vec);
            auto const end = high_resolution_clock::now();

            elapsed_time += (duration_cast<duration<double>>(end - beg)).count();

            if (i != CHECKLOOP) {
                std::rewind(fp.get());
                if (readsize != std::fread(vec.data(), sizeof(std::int32_t), readsize, fp.get())) {
                    throw std::runtime_error("std::freadに失敗");
                }
            }
        }

        ofs << boost::format("%.10f") % (elapsed_time / static_cast<double>(CHECKLOOP)) << ',';
    }
#endif

#if defined(_MSC_VER) && defined(__llvm__)
    std::string myutf8tosjis(std::string_view const & u8str)
    {
        // UTF-8 -> UTF-16
        auto length = ::MultiByteToWideChar(
            CP_UTF8,
            0,
            reinterpret_cast<const char*>(u8str.data()),
            static_cast<int>(u8str.length()),
            nullptr,
            0);
        if (!length) {
            throw std::system_error(std::error_code(GetLastError(), std::system_category()));
        }

        std::wstring temp(length, '\0');

        auto res = ::MultiByteToWideChar(
            CP_UTF8,
            0,
            reinterpret_cast<const char*>(u8str.data()),
            static_cast<int>(u8str.length()),
            temp.data(), temp.length());
        if (!res) {
            throw std::system_error(std::error_code(GetLastError(), std::system_category()));
        }

        // UTF-16 -> Shift-JIS
        length = ::WideCharToMultiByte(CP_ACP, 0,
            temp.data(), static_cast<int>(temp.length()),
            nullptr, 0,
            nullptr, nullptr);

        std::string result(length, '\0');

        res = ::WideCharToMultiByte(CP_ACP, 0,
            temp.data(), static_cast<int>(temp.length()),
            result.data(), static_cast<int>(result.length()),
            nullptr, nullptr);
        if (!res) {
            throw std::system_error(std::error_code(GetLastError(), std::system_category()));
        }

        return result;
    }
#endif

#ifdef DEBUG
    bool vec_check(std::vector<std::int32_t> const & v1, std::vector<std::int32_t> const & v2)
    {
        auto const size = v1.size();
        BOOST_ASSERT(size == v2.size());

        for (auto i = 0UL; i < size; i++) {
            if (v1[i] != v2[i]) {
                std::cerr << "Error! i = " << i << '\n';
                return false;
            }
        }

        return true;
    }
#endif
}


// Copyright Paul A. Bristow 2013

// Use, modification and distribution are subject to 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)

// Program to list all numeric_limits items for any type to a file in Quickbook format.

// C standard http://www.open-std.org/jtc1/sc22/wg11/docs/n507.pdf
// SC22/WG11 N507 DRAFT INTERNATIONAL ISO/IEC STANDARD WD 10967-1
// Information technology Language independent arithmetic Part 1: Integer and Floating point arithmetic

/* E.3 C++
The programming language C++ is defined by ISO/IEC 14882:1998, Programming languages C++ [18].
An implementation should follow all the requirements of LIA-1 unless otherwise specified by
this language binding.
*/

// https://doi.org/10.1109/IEEESTD.1985.82928

// http://www.cesura17.net/~will/Professional/Research/Papers/retrospective.pdf

// http://www.exploringbinary.com/using-integers-to-check-a-floating-point-approximation/

// http://stackoverflow.com/questions/12466745/how-to-convert-float-to-doubleboth-stored-in-ieee-754-representation-without-loss

#ifdef _MSC_VER
#  pragma warning (disable : 4127)  // conditional expression is constant.
#  pragma warning (disable : 4100)  // unreferenced formal parameter.
#endif

#include <iostream>
#include <iomanip>
#include <string>
#include <sstream>
#include <fstream>
#include <limits> // numeric_limits

#include <typeinfo>

#include <boost/version.hpp>
#include <boost/config.hpp>

// May need extra includes for other types, for example:
#include <nil/crypto3/multiprecision/cpp_dec_float.hpp> // is decimal.
#include <nil/crypto3/multiprecision/cpp_bin_float.hpp> // is binary.

// Assume that this will be run on MSVC to get the 32 or 64 bit info.
#ifdef _WIN32
  std::string bits32_64 = "32";
std::string filename = "numeric_limits_32_tables.qbk";
#else
  std::string bits32_64 = "64";
std::string filename = "numeric_limits_64_tables.qbk";
#endif

#ifdef INT32_T_MAX
  int i = INT256_T_MAX;
#endif

std::array<std::string, 16> integer_type_names =
{
"bool",
"char",
"unsigned char",
"char16_t",
"char32_t",
"short",
"unsigned short",
"int",
"unsigned int",
"long",
"unsigned long",
"long long",
"unsigned long long",
"int32_t",
//"uint32_t",
"int64_t",
//"uint64_t",
"int128_t",
//"uint128_t" // Too big?
//"int256_t",
//"uint256_t"
//"int512_t"
};

std::array<std::string, 6> float_type_names =
{
  "function", "float", "double", "long double", "cpp_dec_50", "cpp_bin_128"
};

// Table headings for integer constants.
std::array<std::string, 8> integer_constant_heads =
{
    "type", "signed", "bound", "modulo", "round", "radix", "digits", "digits10", // "max_digits10"
};

// Table headings for integer functions.
std::array<std::string, 2> integer_function_heads =
{
  "max", // "lowest",  assumes is same for all integer types, so not worth listing.
  "min"
};

// Table headings for float constants.
std::array<std::string, 12> float_constant_heads =
{
    "type", // "signed", "exact", "bound", // "modulo",
    "round", "radix", "digits", "digits10", "max_digits10", "min_exp", "min_exp10", "max_exp", "max_exp10", "tiny", "trap"
};

// Table headings for float functions.
std::array<std::string, 10> float_function_heads =
{
  "function", "max", "lowest", "min", "eps", "round", "infinity", "NaN", "sig_NaN", "denorm_min"
};

std::string versions()
{ // Build a string of info about Boost, platform, STL, etc.
  std::stringstream mess;
  //mess << "\n" << "Program:\n\" " __FILE__  << "\"\n"; // \n is mis-interpreted!
  mess << "\n" << "Program:\n numeric_limits_qbk.cpp \n";
#ifdef __TIMESTAMP__
  mess << __TIMESTAMP__;
#endif
  mess << "\nBuildInfo:\n" "  Platform " << BOOST_PLATFORM;
  mess << "\n  Compiler " BOOST_COMPILER;
#ifdef _MSC_FULL_VER
  mess << "\n  MSVC version "<< BOOST_STRINGIZE(_MSC_FULL_VER) << ".";
#endif
  mess << "\n  STL " BOOST_STDLIB;
  mess << "\n  Boost version " << BOOST_VERSION/100000 << "." << BOOST_VERSION/100 % 1000 << "." << BOOST_VERSION % 100 << std::endl;
  return mess.str();
} // std::string versions()

template <typename T>
void out_round_style(std::ostream& os)
{ //! Send short string describing STD::round_style to stream os.
    // os << "Round style is ";
    if (std::numeric_limits<T>::round_style == std::round_indeterminate)
    {
     os << "indeterminate" ;
    }
    else if (std::numeric_limits<T>::round_style == std::round_toward_zero)
    {
      os << "to zero" ;
    }
    else if (std::numeric_limits<T>::round_style == std::round_to_nearest)
    {
      os << "to nearest" ;
    }
    else if (std::numeric_limits<T>::round_style == std::round_toward_infinity)
    {
      os << "to infin[]"; // Or << "to \u221E"  << "to infinity" ;
    }
    else if (std::numeric_limits<T>::round_style == std::round_toward_neg_infinity)
    {
      os << "to -infin[]" ;
    }
    else
    {
      os << "undefined!";
      std::cout << "std::numeric_limits<T>::round_style is undefined value!" << std::endl;
    }
    return;
} // out_round_style(std::ostream& os);

template<typename T>
void integer_constants(std::string type_name, std::ostream& os)
{ //! Output a line of table integer constant values to ostream os.
    os << "\n["
          "[" << type_name << "]" ;
    os << "[" << (std::numeric_limits<T>::is_signed ? "signed" : "unsigned") << "]" ;
    // Is always exact for integer types, so removed:
    // os << "[" << (std::numeric_limits<T>::is_exact ? "exact" : "inexact") << "]" ;
    os << "[" << (std::numeric_limits<T>::is_bounded ? "bound" : "unbounded") << "]" ;
    os << "[" << (std::numeric_limits<T>::is_modulo ? "modulo" : "no") << "]" ;
    os << "[" ; out_round_style<T>(os); os << "]" ;
    os << "[" << std::numeric_limits<T>::radix << "]" ;
    os << "[" << std::numeric_limits<T>::digits << "]" ;
    os << "[" << std::numeric_limits<T>::digits10 << "]" ;
    // Undefined for integers, so removed:
   // os << "[" << std::numeric_limits<T>::max_digits10 << "]"
     os << "]";
} // void integer_constants

template<typename T>
void float_constants(std::string type_name, std::ostream& os)
{ //! Output a row of table values to `ostream` os.
    os << "\n["
          "[" << type_name << "]" ;
    //os << "[" << (std::numeric_limits<T>::is_signed ? "signed" : "unsigned") << "]" ;
    //os << "[" << (std::numeric_limits<T>::is_exact ? "exact" : "inexact") << "]" ;
    //os << "[" << (std::numeric_limits<T>::is_bounded ? "bound" : "no") << "]" ;
    // os << "[" << (std::numeric_limits<T>::is_modulo ? "modulo" : "no") << "]" ;
    os << "[" ; out_round_style<T>(os); os << "]" ;
    os << "[" << std::numeric_limits<T>::radix << "]" ;
    os << "[" << std::numeric_limits<T>::digits << "]" ;
    os << "[" << std::numeric_limits<T>::digits10 << "]" ;
    os << "[" << std::numeric_limits<T>::max_digits10 << "]";
    os << "[" << std::numeric_limits<T>::min_exponent << "]" ;
    os << "[" << std::numeric_limits<T>::min_exponent10 << "]" ;
    os << "[" << std::numeric_limits<T>::max_exponent << "]" ;
    os << "[" << std::numeric_limits<T>::max_exponent10  << "]" ;
    os << "[" << (std::numeric_limits<T>::tinyness_before ? "tiny" : "no") << "]" ;
    os << "[" << (std::numeric_limits<T>::traps ? "traps" : "no") << "]" ;
    os << "]" "\n"; // end of row.
} // void float_constants

/* Types across and two functions down.

template<typename T>
void integer_functions(std::string type_name, std::ostream& os)
{ //! Output a line of table integer function values to `ostream` os.
    os << "\n["
          "[" << type_name << "]" ;
    os << "[" << std::numeric_limits<T>::max() << "]" ;
    //os << "[" << std::numeric_limits<T>::lowest() << "]" ;  always == min for integer types,
    // so removed to save space.
    os << "[" << std::numeric_limits<T>::min() << "]"
      "]";
} // void integer_functions

*/

// Types down and two (or three) functions across.
template<typename T>
void integer_functions(std::string type_name, std::ostream& os)
{ //! Output a line of table integer function values to `ostream` os.
    os << "\n[" // start of row.
          "[" << type_name << "]" ;
    os << "[" << (std::numeric_limits<T>::max)() << "]" ;
   // os << "[" << std::numeric_limits<T>::lowest() << "]" ;
    os << "[" << (std::numeric_limits<T>::min)() << "]" ;
    os <<  "]"; // end of row.
} // void integer_functions

template<typename T>
void float_functions(std::string type_name, std::ostream& os)
{ //! Output a line of table float-point function values to `ostream` os.
    os << "\n[" // start of row.
          "[" << type_name << "]" ;
    os << "[" << (std::numeric_limits<T>::max)() << "]" ;
    os << "[" << (std::numeric_limits<T>::lowest)() << "]" ;
    os << "[" << (std::numeric_limits<T>::min)() << "]"
    os << "[" << std::numeric_limits<T>::epsilon() << "]"
    os << "[" << std::numeric_limits<T>::round_error() << "]"
    os << "[" << std::numeric_limits<T>::infinity() << "]"
    os << "[" << std::numeric_limits<T>::quiet_NaN() << "]"
    os << "[" << std::numeric_limits<T>::signaling_NaN() << "]"
    os << "[" << std::numeric_limits<T>::denorm_min() << "]"
      "]"; // end of row.
} // void float_functions

template<typename T>
int numeric_limits_list(std::string description)
{//!  Output numeric_limits for numeric_limits<T>, for example `numeric_limits_list<bool>()`.
  // This is not used for Quickbook format.
  // std::cout << versions()  << std::endl;

  std::cout << "\n" << description << "\n"  << std::endl; // int, int64_t rather than full long typeid(T).name().

  std::cout << "Type " << typeid(T).name() << " std::numeric_limits::<" << typeid(T).name() << ">\n"  << std::endl;
  // ull long typeid(T).name()

  if (std::numeric_limits<T>::is_specialized == false)
  {
    std::cout << "type " << typeid(T).name()  << " is not specialized for std::numeric_limits!" << std::endl;
    //return -1;
  }

  // Member constants.

  std::cout << (std::numeric_limits<T>::is_signed ? "is signed." : "unsigned.")  << std::endl;
  std::cout << (std::numeric_limits<T>::is_integer ? "is integer." : "not integer (fixed or float-point).")  << std::endl;
  std::cout << (std::numeric_limits<T>::is_exact ? "is exact." : "not exact.")  << std::endl;
  std::cout << (std::numeric_limits<T>::has_infinity ? "has infinity." : "no infinity.")  << std::endl;
  std::cout << (std::numeric_limits<T>::has_quiet_NaN ? "has quiet NaN." : "no quiet NaN.")  << std::endl;
  std::cout << (std::numeric_limits<T>::has_signaling_NaN ? "has signaling NaN." : "no signaling NaN.")  << std::endl;
  if (!std::numeric_limits<T>::is_integer)
  { // is floating_point
    std::cout << "Denorm style is " ;
    if (std::numeric_limits<T>::has_denorm == std::denorm_absent)
    {
      std::cout << "denorm_absent." << std::endl;
    }
    else if (std::numeric_limits<T>::has_denorm == std::denorm_present)
    {
      std::cout << "denorm_present." << std::endl;
    }
    else if (std::numeric_limits<T>::has_denorm == std::denorm_indeterminate)
    {
      std::cout << "denorm_indeterminate." << std::endl;
    }
    else
    {
      std::cout << "std::numeric_limits<T>::has_denorm unexpected value!" << std::endl;
    }

    std::cout << (std::numeric_limits<T>::has_denorm_loss ? "has denorm loss." : "no denorm loss.")  << std::endl;
    // true if a loss of accuracy is detected as a denormalization loss, rather than an inexact result.

    std::cout << "Round style is ";
    if (std::numeric_limits<T>::round_style == std::round_indeterminate)
    {
      std::cout << "round_indeterminate!" << std::endl;
    }
    else if (std::numeric_limits<T>::round_style == std::round_toward_zero)
    {
      std::cout << "round_toward_zero." << std::endl;
    }
    else if (std::numeric_limits<T>::round_style == std::round_to_nearest)
    {
      std::cout << "round_to_nearest." << std::endl;
    }
    else if (std::numeric_limits<T>::round_style == std::round_toward_infinity)
    {
      std::cout << "round_toward_infinity." << std::endl;
    }
    else if (std::numeric_limits<T>::round_style == std::round_toward_neg_infinity)
    {
      std::cout << "round_toward_neg_infinity." << std::endl;
    }
    else
    {
      std::cout << "undefined value!" << std::endl;
    }

  } // is floating_point

  std::cout << (std::numeric_limits<T>::is_iec559 ? "is IEC599." : "not IEC599.")  << std::endl;
  std::cout << (std::numeric_limits<T>::is_bounded ? "is bound." : "unbounded.")  << std::endl;
  std::cout << (std::numeric_limits<T>::is_modulo ? "is modulo." : "no modulo.")  << std::endl;
  std::cout << std::dec << "radix " << std::numeric_limits<T>::radix  << std::endl;
  std::cout << "digits " << std::numeric_limits<T>::digits  << std::endl;
  std::cout << "digits10 " << std::numeric_limits<T>::digits10  << std::endl;

  std::cout.precision(std::numeric_limits<T>::max_digits10); // Full precision for floating-point values like max, min ...

  std::cout << "max_digits10 " << std::numeric_limits<T>::max_digits10  << std::endl;
  std::cout << "min_exponent " << std::numeric_limits<T>::min_exponent  << std::endl;
  std::cout << "min_exponent10 " << std::numeric_limits<T>::min_exponent10  << std::endl;
  std::cout << "max_exponent " << std::numeric_limits<T>::max_exponent  << std::endl;
  std::cout << "max_exponent10 " << std::numeric_limits<T>::max_exponent10  << std::endl;

  std::cout << (std::numeric_limits<T>::tinyness_before ? "Can tiny values before rounding." : "no tinyness_before.")  << std::endl;
  // true if the type can detect tiny values before rounding; false if it cannot.
  std::cout << (std::numeric_limits<T>::traps ? "traps" : "no trapping.")  << std::endl;
  // Whether trapping that reports on arithmetic exceptions is implemented for a type.

  std::cout << "Member functions." << std::endl;
  // (assumes operator<< for type T is available).
  // If floating-point then hex format may not be available.

  std::cout << "max = " << (std::numeric_limits<T>::max)() << std::endl;
  //if (std::numeric_limits<T>::is_integer)
  //{
  //  std::cout << "    = " << std::hex << std::numeric_limits<T>::max() << std::endl;
  //}

  std::cout << "lowest = " << std::dec << std::numeric_limits<T>::lowest() << std::endl;
  //if (std::numeric_limits<T>::is_integer)
  //{
  //   std::cout << "       = " << std::hex << std::numeric_limits<T>::lowest() << std::endl;
  //}

  std::cout << "min = " << (std::dec << std::numeric_limits<T>::min)() << std::endl;
  //if (std::numeric_limits<T>::is_integer)
  //{
  //  std::cout << "    = " << std::hex << std::numeric_limits<T>::min() << std::endl;
  //}

  std::cout << "epsilon = " << std::dec << std::numeric_limits<T>::epsilon() << std::endl;
  //if (std::numeric_limits<T>::is_integer)
  //{
  //  std::cout << "        = " << std::hex << std::numeric_limits<T>::epsilon() << std::endl;
  //}
  // round_error is always zero for integer types.
  // round_error is usually 1/2 = (T)0.5 for floating-point types.
  // round_error is ? for fixed-point.
  std::cout << "round_error = " << std::numeric_limits<T>::round_error() << " ULP." << std::endl;

  std::cout << "infinity = " << std::dec << std::numeric_limits<T>::infinity() << std::endl;
  std::cout << "         = " << std::hex << std::numeric_limits<T>::infinity() << std::endl;

  std::cout << "quiet_NaN = " << std::dec << std::numeric_limits<T>::quiet_NaN() << std::endl;
  std::cout << "          = " << std::hex << std::numeric_limits<T>::quiet_NaN() << std::endl;

  std::cout << "signaling_NaN = " << std::dec << std::numeric_limits<T>::signaling_NaN() << std::endl;
  std::cout << "              = " << std::hex << std::numeric_limits<T>::signaling_NaN() << std::endl;

  //  Only meaningful if (std::numeric_limits<T>::has_denorm == std::denorm_present)
  // so might not bother to show if absent?
  std::cout << "denorm_min = " << std::numeric_limits<T>::denorm_min()  << std::endl;
  std::cout << "           = " << std::numeric_limits<T>::denorm_min()  << std::endl;
  return 0;
}

int main()
{

  try
  {
    using namespace nil::crypto3::multiprecision;

    std::cout << versions() << std::endl;

    std::ofstream fout(filename, std::ios_base::out);
    if (!fout.is_open())
    {
      std::cout << "Unable to open file " << filename << " for output.\n" << std::endl;
      return -1; // boost::EXIT_FAILURE;
    }
    fout <<
      "[/""\n"
      "Copyright 2013 Paul A. Bristow.""\n"
      "Copyright 2013 John Maddock.""\n"
      "Distributed under the Boost Software License, Version 1.0.""\n"
      "(See accompanying file LICENSE_1_0.txt or copy at""\n"
      "http://www.boost.org/LICENSE_1_0.txt).""\n"
      "]""\n"
    << std::endl;

    fout << "[section:limits"<< bits32_64 << " Numeric limits for " << bits32_64 << "-bit platform]" << std::endl;

    // Output platform version info (32 or 64).
    fout << "These tables were generated using the following program and options:\n\n"
      "[pre""\n"
      << versions()
      << "]""\n"
      << std::endl;

    fout << "[table:integral_constants Integer types constants (`std::numeric_limits<T>::is_integer == true` && is_exact == true)" "\n"
      "[";

    for (size_t i = 0; i < integer_constant_heads.size(); i++)
    { // signed, bound, modulo ...
      fout << "[" << integer_constant_heads[i] << "]" ;
    }
    fout << "]";

    integer_constants<bool>("bool", fout);
    integer_constants<char>("char", fout);
    integer_constants<unsigned char>("unsigned char", fout);
    integer_constants<char16_t>("char16_t", fout);
    integer_constants<char32_t>("char32_t", fout);
    integer_constants<short>("short", fout);
    integer_constants<unsigned short>("unsigned short", fout);
    integer_constants<int>("int", fout);
    integer_constants<unsigned int>("unsigned", fout);
    integer_constants<long>("long", fout);
    integer_constants<unsigned long>("unsigned long", fout);
    integer_constants<long long>("long long", fout);
    integer_constants<unsigned long long>("unsigned long long", fout);
    integer_constants<int32_t>("int32_t", fout);
    integer_constants<uint32_t>("uint32_t", fout);
    integer_constants<int64_t>("int64_t", fout);
    integer_constants<uint64_t>("uint64_t", fout);
    integer_constants<int128_t>("int128_t", fout);
    integer_constants<uint128_t>("uint128_t", fout);
    integer_constants<int256_t>("int256_t", fout);
    integer_constants<uint256_t>("uint256_t", fout);
   // integer_constants<int512_t>("int512_t", fout);
    //integer_constants<uint512_t>("uint512_t", fout); // Too big?
    integer_constants<cpp_int>("cpp_int", fout);

    fout << "\n]\n\n";

    fout << "[table:integral_functions Integer types functions (`std::numeric_limits<T>::is_integer == true && std::numeric_limits<T>::min() == std::numeric_limits<T>::lowest()` )" "\n"
      "[";
    // Display types across the page, and 2 (or 3) functions as rows.

    fout << "[function]";
    for (size_t i = 0; i < integer_function_heads.size(); i++)
    {
      fout << "[" << integer_function_heads[i] << "]" ;
    }
    fout << "]"; // end of headings.
    integer_functions<bool>("bool", fout);
    //integer_functions<char>("char", fout); // Need int value not char.
    fout << "\n[" // start of row.
       "[" << "char"<< "]" ;
    fout << "[" << static_cast<int>(std::numeric_limits<char>::max)() << "]" ;
   // fout << "[" << (std::numeric_limits<T>::lowest)() << "]" ;
    fout << "[" << static_cast<int>(std::numeric_limits<char>::min)() << "]" ;
    fout <<  "]"; // end of row.
    //integer_functions<unsigned char>("unsigned char", fout); // Need int value not char.
    fout << "\n[" // start of row.
       "[" << "unsigned char"<< "]" ;
    fout << "[" << static_cast<int>(std::numeric_limits<unsigned char>::max)() << "]" ;
   // fout << "[" << std::numeric_limits<unsigned char>::lowest() << "]" ;
    fout << "[" << static_cast<int>(std::numeric_limits<unsigned char>::min)() << "]" ;
    fout <<  "]"; // end of row.

    integer_functions<char16_t>("char16_t", fout);
    integer_functions<char32_t>("char32_t", fout);
    integer_functions<short>("short", fout);
    integer_functions<unsigned short>("unsigned short", fout);
    integer_functions<int>("int", fout);
    integer_functions<unsigned int>("unsigned int", fout);
    integer_functions<long>("long", fout);
    integer_functions<unsigned long>("unsigned long", fout);
    integer_functions<long long>("long long", fout);
    integer_functions<unsigned long long>("unsigned long long", fout);
    integer_functions<int32_t>("int32_t", fout);
    integer_functions<int64_t>("int64_t", fout);
    integer_functions<int128_t>("int128_t", fout);
    fout << "]" "\n";  // end of table;

    //fout << "[[max]"
    //  << "[" << std::numeric_limits<bool>::max() << "]"
    //  << "[" << static_cast<int>(std::numeric_limits<char>::max()) << "]"
    //  << "[" << static_cast<int>(std::numeric_limits<unsigned char>::max()) << "]"
    //  << "[" << static_cast<int>(std::numeric_limits<char16_t>::max()) << "]"
    //  << "[" << static_cast<int>(std::numeric_limits<char32_t>::max()) << "]"
    //  << "[" << std::numeric_limits<short>::max() << "]"
    //  << "[" << std::numeric_limits<unsigned short>::max() << "]"
    //  << "[" << std::numeric_limits<int>::max() << "]"
    //  << "[" << std::numeric_limits<unsigned int>::max() << "]"
    //  << "[" << std::numeric_limits<long>::max() << "]"
    //  << "[" << std::numeric_limits<unsigned long>::max() << "]"
    //  << "[" << std::numeric_limits<long long>::max() << "]"
    //  << "[" << std::numeric_limits<unsigned long long>::max() << "]"
    //  << "[" << std::numeric_limits<int32_t>::max() << "]"
    //  << "[" << std::numeric_limits<int64_t>::max() << "]"
    //  << "[" << std::numeric_limits<int128_t>::max() << "]"
    //  //<< "[" << std::numeric_limits<int256_t>::max() << "]"  // too big?
    //  //<< "[" << std::numeric_limits<int512_t>::max() << "]" // too big?
    //  << "]" "\n";
    ///*  Assume lowest() is not useful as == min for all integer types.
    // */

    //fout << "[[min]"
    //  << "[" << std::numeric_limits<bool>::min() << "]"
    //  << "[" << static_cast<int>(std::numeric_limits<char>::min()) << "]"
    //  << "[" << static_cast<int>(std::numeric_limits<unsigned char>::min()) << "]"
    //  << "[" << static_cast<int>(std::numeric_limits<char16_t>::min()) << "]"
    //  << "[" << static_cast<int>(std::numeric_limits<char32_t>::min()) << "]"
    //  << "[" << std::numeric_limits<short>::min() << "]"
    //  << "[" << std::numeric_limits<unsigned short>::min() << "]"
    //  << "[" << std::numeric_limits<int>::min() << "]"
    //  << "[" << std::numeric_limits<unsigned int>::min() << "]"
    //  << "[" << std::numeric_limits<long>::min() << "]"
    //  << "[" << std::numeric_limits<unsigned long>::min() << "]"
    //  << "[" << std::numeric_limits<long long>::min() << "]"
    //  << "[" << std::numeric_limits<unsigned long long>::min() << "]"
    //  << "[" << std::numeric_limits<int32_t>::min() << "]"
    //  << "[" << std::numeric_limits<int64_t>::min() << "]"
    //  << "[" << std::numeric_limits<int128_t>::min() << "]"
    //  // << "[" << std::numeric_limits<int256_t>::min() << "]"  // too big?
    //  // << "[" << std::numeric_limits<int512_t>::min() << "]"  // too big?
    //  << "]""\n";

  // Floating-point

    typedef number<cpp_dec_float<50> > cpp_dec_float_50; // 50 decimal digits.
    typedef number<cpp_bin_float<113> > bin_128bit_double_type; // == Binary rare long double.

    fout <<
      //"[table:float_functions Floating-point types constants (`std::numeric_limits<T>::is_integer == false && std::numeric_limits<T>::is_modulo == false` )" "\n"
      "[table:float_functions Floating-point types constants (`std::numeric_limits<T>::is_integer==false && is_signed==true && is_modulo==false && is_exact==false && is_bound==true`)" "\n"
      "[";
    for (size_t i = 0; i < float_constant_heads.size(); i++)
    {
      fout << "[" << float_constant_heads[i] << "]" ;
    }
    fout << "]"; // end of headings.

    float_constants<float>("float", fout);
    float_constants<double>("double", fout);
    float_constants<long double>("long double", fout);
    float_constants<cpp_dec_float_50>("cpp_dec_float_50", fout);
    float_constants<bin_128bit_double_type>("bin_128bit_double_type", fout);
    fout << "]" "\n";  // end of table;

    fout <<
      "[table:float_functions Floating-point types functions (`std::numeric_limits<T>::is_integer == false`)" "\n"
      "[";

    for (size_t i = 0; i < float_type_names.size(); i++)
    {
      fout << "[" << float_type_names[i] << "]" ;
    }
    fout << "]"; // end of headings.

    fout << "[[max]"
      << "[" << (std::numeric_limits<float>::max)() << "]"
      << "[" << (std::numeric_limits<double>::max)() << "]"
//#if LDBL_MANT_DIG > DBL_MANT_DIG
    // Perhaps to test Long double is not just a duplication of double (but need change is headings too).
      << "[" << (std::numeric_limits<long double>::max)() << "]"
//#endif
      << "[" << (std::numeric_limits<cpp_dec_float_50>::max)() << "]"
      << "[" << (std::numeric_limits<bin_128bit_double_type >::max)() << "]"
      << "]" "\n"; // end of row.

    fout << "[[min]"
      << "[" << (std::numeric_limits<float>::min)() << "]"
      << "[" << (std::numeric_limits<double>::min)() << "]"
//#if LDBL_MANT_DIG > DBL_MANT_DIG
    // Long double is not just a duplication of double.
      << "[" << (std::numeric_limits<long double>::min)() << "]"
//#endif
      << "[" << (std::numeric_limits<cpp_dec_float_50 >::min)() << "]"
      << "[" << (std::numeric_limits<bin_128bit_double_type >::min)() << "]"
      << "]" "\n"; // end of row.

    fout << "[[epsilon]"
      << "[" << std::numeric_limits<float>::epsilon() << "]"
      << "[" << std::numeric_limits<double>::epsilon() << "]"
//#if LDBL_MANT_DIG > DBL_MANT_DIG
    // Long double is not just a duplication of double.
      << "[" << std::numeric_limits<long double>::epsilon() << "]"
//#endif
      << "[" << std::numeric_limits<cpp_dec_float_50 >::epsilon() << "]"
      << "[" << std::numeric_limits<bin_128bit_double_type >::epsilon() << "]"
      << "]" "\n"; // end of row.

    fout << "[[round_error]"
      << "[" << std::numeric_limits<float>::round_error() << "]"
      << "[" << std::numeric_limits<double>::round_error() << "]"
//#if LDBL_MANT_DIG > DBL_MANT_DIG
    // Long double is not just a duplication of double.
      << "[" << std::numeric_limits<long double>::round_error() << "]"
//#endif
      << "[" << std::numeric_limits<cpp_dec_float_50 >::round_error() << "]"
      << "[" << std::numeric_limits<bin_128bit_double_type >::round_error() << "]"
      << "]" "\n"; // end of row.

    fout << "[[infinity]"
      << "[" << std::numeric_limits<float>::infinity() << "]"
      << "[" << std::numeric_limits<double>::infinity() << "]"
//#if LDBL_MANT_DIG > DBL_MANT_DIG
    // Long double is not just a duplication of double.
      << "[" << std::numeric_limits<long double>::infinity() << "]"
//#endif
      << "[" << std::numeric_limits<cpp_dec_float_50 >::infinity() << "]"
      << "[" << std::numeric_limits<bin_128bit_double_type >::infinity() << "]"
      << "]" "\n"; // end of row.

    fout << "[[quiet_NaN]"
      << "[" << std::numeric_limits<float>::quiet_NaN() << "]"
      << "[" << std::numeric_limits<double>::quiet_NaN() << "]"
//#if LDBL_MANT_DIG > DBL_MANT_DIG
    // Long double is not just a duplication of double.
      << "[" << std::numeric_limits<long double>::quiet_NaN() << "]"
//#endif
      << "[" << std::numeric_limits<cpp_dec_float_50 >::quiet_NaN() << "]"
      << "[" << std::numeric_limits<bin_128bit_double_type >::quiet_NaN() << "]"
      << "]" "\n"; // end of row.

    fout << "[[signaling_NaN]"
      << "[" << std::numeric_limits<float>::signaling_NaN() << "]"
      << "[" << std::numeric_limits<double>::signaling_NaN() << "]"
//#if LDBL_MANT_DIG > DBL_MANT_DIG
    // Long double is not just a duplication of double.
      << "[" << std::numeric_limits<long double>::signaling_NaN() << "]"
//#endif
      << "[" << std::numeric_limits<cpp_dec_float_50 >::signaling_NaN() << "]"
      << "[" << std::numeric_limits<bin_128bit_double_type >::signaling_NaN() << "]"
      << "]" "\n"; // end of row.

    fout << "[[denorm_min]"
      << "[" << std::numeric_limits<float>::denorm_min() << "]"
      << "[" << std::numeric_limits<double>::denorm_min() << "]"
//#if LDBL_MANT_DIG > DBL_MANT_DIG
    // Long double is not just a duplication of double.
      << "[" << std::numeric_limits<long double>::denorm_min() << "]"
//#endif
      << "[" << std::numeric_limits<cpp_dec_float_50 >::denorm_min() << "]"
      << "[" << std::numeric_limits<bin_128bit_double_type >::denorm_min() << "]"
      << "]" "\n"; // end of row.

     fout << "]" "\n";  // end of table;

       fout <<  "\n\n"
    "[endsect] [/section:limits32  Numeric limits for 32-bit platform]" "\n" << std::endl;

    fout.close();
  }
  catch(std::exception ex)
  {
    std::cout << "exception thrown: " << ex.what() << std::endl;
  }

} // int main()

/*
  Description: Autorun "J:\Cpp\Misc\Debug\numeric_limits_qbk.exe"

  Program: I:\boost-sandbox\multiprecision.cpp_bin_float\libs\multiprecision\doc\numeric_limits_qbk.cpp
  Wed Aug 28 14:17:21 2013
  BuildInfo:
    Platform Win32
    Compiler Microsoft Visual C++ version 10.0
    MSVC version 160040219.
    STL Dinkumware standard library version 520
    Boost version 1.55.0

  */


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

  @copyright 2016 NumScale SAS

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

**/
//==================================================================================================
#ifndef BOOST_SIMD_OPERATOR_HPP_INCLUDED
#define BOOST_SIMD_OPERATOR_HPP_INCLUDED

namespace boost { namespace simd
{
  /*!
    @ingroup group-functions
    @defgroup group-operator Operators

    These functions provide scalar and SIMD version of the native C and C++ operators.

    Operators are provided in infix and prefix notations, except for scalar floating types
    as C++ does not allow overloading of operators for these types. Operators &, |, ~,  % and
    the ternary operator ?: are also not defined for floating types in C++.
    In these seldomly used cases, prefix bitwise_and, bitwise_or, complement
    and if_else are required.

    @warningbox{Note that && and || overloaded on simd packs do not make short circuitry}

    Here is the list of of operators and their infix associated notation.
    These remarks are only valid if the prefix notation is used and at least one parameter is a pack:
    otherwise infix operators on scalars are regulated by usual C++ langage rules.

    All operators are included as soon as <boost/simd/pack.hpp> is included.

    <center>
    | Name                    | op  | arity |   types      |  precondition/result                          |
    |-------------------------|-----|-------|--------------|-----------------------------------------------|
    | @ref bitwise_and        | &   |  2    |  T1, T2      |  same bit size for T1 and T2    (1)           |
    | @ref bitwise_or         | \|  |  2    |  T1, T2      |  same bit size for T1 and T2    (1)           |
    | @ref bitwise_xor        | ^   |  2    |  T1, T2      |  same bit size for T1 and T2    (1)           |
    | @ref complement         | ~   |  1    |  T1          |                                               |
    | @ref divides            | /   |  2    |  T1, T1      |  arithmetic types               (2)           |
    | @ref div                | /   |  2    |  T1, T1      |  arithmetic types               (2)           |
    | @ref if_else            | NA  |  3    |  T1, T2, T2  |                                               |
    | @ref is_equal           | ==  |  2    |  T1, T1      |                                               |
    | @ref is_greater         | >   |  2    |  T1, T1      |                                               |
    | @ref is_greater_equal   | >=  |  2    |  T1, T1      |                                               |
    | @ref is_less            | <   |  2    |  T1, T1      |                                               |
    | @ref is_less_equal      | <=  |  2    |  T1, T1      |                                               |
    | @ref is_not_equal       | !=  |  2    |  T1, T1      |                                               |
    | @ref logical_and        | &&  |  2    |  T1, T1      |  returns a @ref logical                       |
    | @ref logical_not        | !   |  1    |  T1, T1      |  returns a @ref logical                       |
    | @ref logical_or         | \|\||  2    |  T1, T1      |  returns a @ref logical                      |
    | @ref minus              | -   |  2    |  T1, T1      |  arithmetic types                (2)          |
    | @ref rem                | %   |  2    |  T1, T2      |  T2 is integral scalar or associated to T1 (3)|
    | @ref multiplies         | *   |  2    |  T1, T1      |  arithmetic types                (2)          |
    | @ref plus               | +   |  2    |  T1, T1      |  arithmetic types                (2)          |
    | @ref shift_left         | <<  |  2    |  T1, T2      |  T2 is integral scalar or associated to T1 (3)|
    | @ref shift_right        | >>  |  2    |  T1, T2      |  T2 is integral scalar or associated to T1 (3)|
    | @ref shr (logical shift)|     |  2    |  T1, T2      |  T2 is integral scalar or associated to T1 (3)|
    | @ref unary_minus        | -   |  1    |  T1          |  signed arithmetic types         (2)          |
    | @ref unary_plus         | +   |  1    |  T1          |  arithmetic types                (2)          |
    </center>

    Notes:
     - (1)  This precisely means sizeof(T1) == sizeof(T2) or one is scalar and its size in bits is the same as
           the element of the other (which is a pack).

     - (2)  arithmetic types are defined as std::int8_t, std::int16_t, std::int32_t, std::int64_t,
           std::uint8_t, std::uint16_t, std::uint32_t, std::uint64_t, float, double and packs of these.

           The use of implementation dependent types as char, int, long, long long and their unsigned conterpart
           is discouraged and even may lead to inconsistent behaviour.

           Also it must be noted that "arithmetic" operators cannot mix types. This is to ensure correct SIMD
           performance if the operation is supported by hardware.

     - (3)  This precisely means T2 is both scalar and of integral type or T2 is as_integer_t<T1>. Note that if T2 scalar
           is always simd hardware supported, the other case is often emulated.

   Extensions:

      @ref div "division"  and @ref rem "remainder" operators have extensions to be seen following the links.

  **/
} }

#include <boost/simd/function/bitwise_and.hpp>
#include <boost/simd/function/bitwise_or.hpp>
#include <boost/simd/function/bitwise_xor.hpp>
#include <boost/simd/function/complement.hpp>
#include <boost/simd/function/div.hpp>
#include <boost/simd/function/divides.hpp>
#include <boost/simd/function/minus.hpp>
#include <boost/simd/function/multiplies.hpp>
#include <boost/simd/function/plus.hpp>
#include <boost/simd/function/shift_left.hpp>
#include <boost/simd/function/shift_right.hpp>
#include <boost/simd/function/shl.hpp>
#include <boost/simd/function/unary_minus.hpp>
#include <boost/simd/function/unary_plus.hpp>
#include <boost/simd/function/if_else.hpp>
#include <boost/simd/function/is_equal.hpp>
#include <boost/simd/function/is_greater.hpp>
#include <boost/simd/function/is_greater_equal.hpp>
#include <boost/simd/function/is_less.hpp>
#include <boost/simd/function/is_less_equal.hpp>
#include <boost/simd/function/is_not_equal.hpp>
#include <boost/simd/function/logical_and.hpp>
#include <boost/simd/function/logical_not.hpp>
#include <boost/simd/function/logical_or.hpp>
#include <boost/simd/function/minus.hpp>
#include <boost/simd/function/rem.hpp>
#include <boost/simd/function/multiplies.hpp>
#include <boost/simd/function/plus.hpp>
#include <boost/simd/function/shift_left.hpp>
#include <boost/simd/function/shift_right.hpp>
#include <boost/simd/function/shl.hpp>
#include <boost/simd/function/unary_minus.hpp>
#include <boost/simd/function/unary_plus.hpp>

#endif


// normalEqn.hpp
#ifndef COURSERA_NORMALEQN_HPP
#define COURSERA_NORMALEQN_HPP

#include <memory>
#include <armadillo>

void normalEqn(std::shared_ptr<arma::fvec> &theta,
               const std::shared_ptr<arma::fmat> &X, const std::shared_ptr<arma::fvec> &y);

#endif // COURSERA_NORMALEQN_HPP