Program Listing for File progress_bar.cc

Return to documentation for file (core/progress_bar.cc)

#include "lupnt/core/progress_bar.h"

#include "lupnt/core/error.h"

namespace lupnt {

  std::vector<ProgressBar*> ProgressBar::progress_bars_;

  ProgressBar::ProgressBar(int total)
      : total_(total), start_time_(std::chrono::system_clock::now()), last_update_(start_time_) {
    row_ = static_cast<int>(progress_bars_.size());
    std::cout << std::endl;
    Redraw();
    for (auto bar : progress_bars_) bar->Redraw();
    progress_bars_.push_back(this);
  }

  void ProgressBar::SetDescription(const std::string& description) {
    description_ = description + " ";
    Redraw();
  }

  void ProgressBar::Reset() {
    current_value_ = 0;
    current_progress_ = 0;
    value_at_last_update_ = 0;
    start_time_ = std::chrono::system_clock::now();
    last_update_ = start_time_;
    Redraw();
  }

  void ProgressBar::Update() { Update(current_value_ + 1); }

  void ProgressBar::Update(int value) {
    LUPNT_CHECK(value >= 0 && value <= total_, "Value out of range", "ProgressBar");
    cursor_hidden_ = true;
    current_value_ = value;
    auto now = std::chrono::system_clock::now();
    std::chrono::duration<double> elapsed_since_last_update = now - last_update_;
    int new_progress = static_cast<int>(static_cast<double>(value) / total_ * 100);

    if (new_progress > current_progress_ && elapsed_since_last_update.count() >= max_update_freq_) {
      std::chrono::duration<double> elapsed_since_start = now - start_time_;
      double iters_per_sec = value / elapsed_since_start.count();
      int remaining_time
          = iters_per_sec > 0 ? static_cast<int>((total_ - value) / iters_per_sec) : 0;

      current_progress_ = new_progress;
      last_update_ = now;
      value_at_last_update_ = value;

      Display(value, iters_per_sec, remaining_time, elapsed_since_start.count());
    }

    if (value == total_ && !leave_) Finish();
  }

  void ProgressBar::Redraw() {
    auto now = std::chrono::system_clock::now();
    std::chrono::duration<double> elapsed_since_start = now - start_time_;
    double iters_per_sec = current_value_ / elapsed_since_start.count();
    int remaining_time
        = iters_per_sec > 0 ? static_cast<int>((total_ - current_value_) / iters_per_sec) : 0;
    Display(current_value_, iters_per_sec, remaining_time, elapsed_since_start.count());
  }

  void ProgressBar::Finish() {
    auto now = std::chrono::system_clock::now();
    current_value_ = total_;
    current_progress_ = 100;
    std::chrono::duration<double> elapsed_since_start = now - start_time_;
    double total_iters_per_sec = total_ / elapsed_since_start.count();
    Display(total_, total_iters_per_sec, 0, elapsed_since_start.count());
#pragma omp critical
    {
      if (std::find(progress_bars_.begin(), progress_bars_.end(), this) != progress_bars_.end()) {
        progress_bars_.erase(std::remove(progress_bars_.begin(), progress_bars_.end(), this),
                             progress_bars_.end());
      }
    }
  }

  bool ProgressBar::IsDone() const { return current_value_ == total_; }

  ProgressBar::~ProgressBar() {
    // if (cursor_hidden_) std::cout << "\033[?25h" << std::flush;  // Show cursor
  }

  void ProgressBar::Display(int value, double speed, int remaining_time, double elapsed) {
#pragma omp critical
    {
      std::stringstream ss;
      std::cout << "\033[1A" << std::flush;  // Move up
      if (row_ > 0) {
        std::cout << "\033[" << row_ << "A" << std::flush;  // Move up
      }
      // Use ANSI escape code to clear the entire line
      std::cout << "\r\033[2K" << std::flush;  // Clear line

      // Draw bar
      if (!description_.empty()) ss << description_;
      ss << std::setw(3) << current_progress_ << "%|";
      int pos = static_cast<int>(static_cast<double>(current_value_) / static_cast<double>(total_)
                                 * static_cast<double>(bar_width_));
      // LUPNT_CHECK(pos >= 0 && pos <= bar_width_, "Position out of range", "ProgressBar");
      pos = std::min(std::max(pos, 0), bar_width_);
      for (int i = 0; i < pos; ++i) ss << "\u2588";
      for (int i = pos; i < bar_width_; ++i) ss << " ";
      ss << "| " << value << "/" << total_ << " [";
      ss << FormatTime(static_cast<int>(elapsed)) << "<";
      ss << FormatTime(remaining_time) << ", ";
      ss << std::fixed << std::setprecision(1) << speed << " it/s]";
      std::cout << ss.str() << std::endl;

      // Move down
      if (row_ > 0) {
        std::cout << "\033[" << row_ << "B" << std::flush;
      }
    }
  }

  std::string ProgressBar::FormatTime(int seconds) {
    std::stringstream ss;
    int hours = seconds / 3600;
    int minutes = (seconds % 3600) / 60;
    seconds %= 60;

    if (hours > 0) ss << hours << ":";
    ss << std::setw(2) << std::setfill('0') << minutes << ":";
    ss << std::setw(2) << std::setfill('0') << seconds;

    return ss.str();
  }

}  // namespace lupnt