text: //  Copyright (c) 2011-2012 Bryce Adelstein-Lelbach
  //  Copyright (c) 2007-2012 Hartmut Kaiser
  //
  //  SPDX-License-Identifier: BSL-1.0
  //  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 <pika/modules/program_options.hpp>
  
  #include <cstddef>
  #include <cstdint>
  #include <functional>
  #include <iostream>
  #include <numeric>
  #include <random>
  #include <stdexcept>
  #include <vector>
  #include <utility>
  
  using pika::program_options::variables_map;
  using pika::program_options::options_description;
  using pika::program_options::value;
  using pika::program_options::store;
  using pika::program_options::command_line_parser;
  using pika::program_options::notify;
  
  ///////////////////////////////////////////////////////////////////////////////
  // Command-line variables.
  std::uint64_t tasks = 500000;
  std::uint64_t min_delay = 0;
  std::uint64_t max_delay = 0;
  std::uint64_t total_delay = 0;
  std::uint64_t seed = 0;
  
  ///////////////////////////////////////////////////////////////////////////////
  std::uint64_t shuffler(
      std::mt19937_64& prng
    , std::uint64_t high
      )
  {
      if (high == 0)
          throw std::logic_error("high value was 0");
  
      // Our range is [0, x).
      std::uniform_int_distribution<std::uint64_t>
          dist(0, high - 1);
  
      return dist(prng);
  }
  
  ///////////////////////////////////////////////////////////////////////////////
  int app_main(
      variables_map&
      )
  {
      ///////////////////////////////////////////////////////////////////////
      // Initialize the PRNG seed.
      if (!seed)
          seed = std::uint64_t(std::time(nullptr));
  
      ///////////////////////////////////////////////////////////////////////
      // Validate command-line arguments.
      if (0 == tasks)
          throw std::invalid_argument("count of 0 tasks specified\n");
  
      if (min_delay > max_delay)
          throw std::invalid_argument("minimum delay cannot be larger than "
                                      "maximum delay\n");
  
      if (min_delay > total_delay)
          throw std::invalid_argument("minimum delay cannot be larger than"
                                      "total delay\n");
  
      if (max_delay > total_delay)
          throw std::invalid_argument("maximum delay cannot be larger than "
                                      "total delay\n");
  
      if ((min_delay * tasks) > total_delay)
          throw std::invalid_argument("minimum delay is too small for the "
                                      "specified total delay and number of "
                                      "tasks\n");
  
      if ((max_delay * tasks) < total_delay)
          throw std::invalid_argument("maximum delay is too small for the "
                                      "specified total delay and number of "
                                      "tasks\n");
  
      ///////////////////////////////////////////////////////////////////////
      // Randomly generate a description of the heterogeneous workload.
      std::vector<std::uint64_t> payloads;
      payloads.reserve(tasks);
  
      // For random numbers, we use a 64-bit specialization of stdlib's
      // mersenne twister engine (good uniform distribution up to 311
      // dimensions, cycle length 2 ^ 19937 - 1)
      std::mt19937_64 prng(seed);
  
      std::uint64_t current_sum = 0;
  
      for (std::uint64_t i = 0; i < tasks; ++i)
      {
          // Credit to Spencer Ruport for putting this algorithm on
          // stackoverflow.
          std::uint64_t const low_calc
              = (total_delay - current_sum) - (max_delay * (tasks - 1 - i));
  
          bool const negative
              = (total_delay - current_sum) < (max_delay * (tasks - 1 - i));
  
          std::uint64_t const low
              = (negative || (low_calc < min_delay)) ? min_delay : low_calc;
  
          std::uint64_t const high_calc
              = (total_delay - current_sum) - (min_delay * (tasks - 1 - i));
  
          std::uint64_t const high
              = (high_calc > max_delay) ? max_delay : high_calc;
  
          // Our range is [low, high].
          std::uniform_int_distribution<std::uint64_t>
              dist(low, high);
  
          std::uint64_t const payload = dist(prng);
  
          if (payload < min_delay)
              throw std::logic_error("task delay is below minimum");
  
          if (payload > max_delay)
              throw std::logic_error("task delay is above maximum");
  
          current_sum += payload;
          payloads.push_back(payload);
      }
  
      // Randomly shuffle the entire sequence to deal with drift.
      std::random_device random_device;
      std::mt19937 generator(random_device());
      std::shuffle(payloads.begin(), payloads.end(), std::move(generator));
  
      ///////////////////////////////////////////////////////////////////////
      // Validate the payloads.
      if (payloads.size() != tasks)
          throw std::logic_error("incorrect number of tasks generated");
  
      std::uint64_t const payloads_sum =
          std::accumulate(payloads.begin(), payloads.end(), 0ULL);
      if (payloads_sum != total_delay)
          throw std::logic_error("incorrect total delay generated");
  
      for (std::size_t i = 0; i < payloads.size(); ++i)
          std::cout << payloads[i] << "\n";
  
      return 0;
  }
  
  ///////////////////////////////////////////////////////////////////////////////
  int main(
      int argc
    , char* argv[]
      )
  {
      ///////////////////////////////////////////////////////////////////////////
      // Parse command line.
      variables_map vm;
  
      options_description cmdline("Usage: " PIKA_APPLICATION_STRING " [options]");
  
      cmdline.add_options()
          ( "help,h"
          , "print out program usage (this message)")
  
          ( "tasks"
          , value<std::uint64_t>(&tasks)->default_value(500000)
          , "number of tasks to invoke")
  
          ( "min-delay"
          , value<std::uint64_t>(&min_delay)->default_value(0)
          , "minimum number of iterations in the delay loop")
  
          ( "max-delay"
          , value<std::uint64_t>(&max_delay)->default_value(0)
          , "maximum number of iterations in the delay loop")
  
          ( "total-delay"
          , value<std::uint64_t>(&total_delay)->default_value(0)
          , "total number of delay iterations to be executed")
  
          ( "seed"
          , value<std::uint64_t>(&seed)->default_value(0)
          , "seed for the pseudo random number generator (if 0, a seed is "
            "chosen based on the current system time)")
          ;
  
      store(command_line_parser(argc, argv).options(cmdline).run(), vm);
  
      notify(vm);
  
      // Print help screen.
      if (vm.count("help"))
      {
          std::cout << cmdline;
          return 0;
      }
  
      return app_main(vm);
  }
  
  