1 // Copyright (C) 2010  Davis E. King (davis@dlib.net)
2 // License: Boost Software License   See LICENSE.txt for the full license.
3 #ifndef DLIB_CONSOLE_PROGRESS_INDiCATOR_Hh_
4 #define DLIB_CONSOLE_PROGRESS_INDiCATOR_Hh_
5 
6 #include <ctime>
7 #include <cmath>
8 #include <limits>
9 #include <iostream>
10 
11 namespace dlib
12 {
13 
14 // ----------------------------------------------------------------------------------------
15 
16     class console_progress_indicator
17     {
18         /*!
19             WHAT THIS OBJECT REPRESENTS
20                 This object is a tool for reporting how long a task will take
21                 to complete.
22 
23                 For example, consider the following bit of code:
24 
25                     console_progress_indicator pbar(100)
26                     for (int i = 1; i <= 100; ++i)
27                     {
28                         pbar.print_status(i);
29                         long_running_operation();
30                     }
31 
32                 The above code will print a message to the console each iteration
33                 which shows how much time is remaining until the loop terminates.
34         !*/
35 
36     public:
37 
38         inline explicit console_progress_indicator (
39             double target_value
40         );
41         /*!
42             ensures
43                 - #target() == target_value
44         !*/
45 
46         inline void reset (
47             double target_value
48         );
49         /*!
50             ensures
51                 - #target() == target_value
52                 - performs the equivalent of:
53                     *this = console_progress_indicator(target_value)
54                     (i.e. resets this object with a new target value)
55 
56         !*/
57 
58         inline double target (
59         ) const;
60         /*!
61             ensures
62                 - This object attempts to measure how much time is
63                   left until we reach a certain targeted value.  This
64                   function returns that targeted value.
65         !*/
66 
67         inline bool print_status (
68             double cur,
69             bool always_print = false
70         );
71         /*!
72             ensures
73                 - print_status() assumes it is called with values which are linearly
74                   approaching target().  It will attempt to predict how much time is
75                   remaining until cur becomes equal to target().
76                 - prints a status message to the screen which indicates how much
77                   more time is left until cur is equal to target()
78                 - if (always_print) then
79                     - This function prints to the screen each time it is called.
80                 - else
81                     - This function throttles the printing so that at most 1 message is
82                       printed each second.  Note that it won't print anything to the screen
83                       until about one second has elapsed.  This means that the first call
84                       to print_status() never prints to the screen.
85                 - This function returns true if it prints to the screen and false
86                   otherwise.
87         !*/
88 
89     private:
90 
91         double target_val;
92 
93         time_t start_time;
94         double first_val;
95         double seen_first_val;
96         time_t last_time;
97 
98     };
99 
100 // ----------------------------------------------------------------------------------------
101 // ----------------------------------------------------------------------------------------
102 //                               IMPLEMENTATION DETAILS
103 // ----------------------------------------------------------------------------------------
104 // ----------------------------------------------------------------------------------------
105 
106     console_progress_indicator::
console_progress_indicator(double target_value)107     console_progress_indicator (
108         double target_value
109     ) :
110         target_val(target_value),
111         start_time(0),
112         first_val(0),
113         seen_first_val(false),
114         last_time(0)
115     {
116     }
117 
118 // ----------------------------------------------------------------------------------------
119 
120     bool console_progress_indicator::
print_status(double cur,bool always_print)121     print_status (
122         double cur,
123         bool always_print
124     )
125     {
126         const time_t cur_time = std::time(0);
127 
128         // if this is the first time print_status has been called
129         // then collect some information and exit.  We will print status
130         // on the next call.
131         if (!seen_first_val)
132         {
133             start_time = cur_time;
134             last_time = cur_time;
135             first_val = cur;
136             seen_first_val = true;
137             return false;
138         }
139 
140         if (cur_time != last_time || always_print)
141         {
142             last_time = cur_time;
143             double delta_t = static_cast<double>(cur_time - start_time);
144             double delta_val = std::abs(cur - first_val);
145 
146             // don't do anything if cur is equal to first_val
147             if (delta_val < std::numeric_limits<double>::epsilon())
148                 return false;
149 
150             double seconds = delta_t/delta_val * std::abs(target_val - cur);
151 
152             std::ios::fmtflags oldflags = std::cout.flags();
153 
154             std::cout.setf(std::ios::fixed,std::ios::floatfield);
155             std::streamsize ss;
156 
157             if (seconds < 60)
158             {
159                 ss = std::cout.precision(0);
160                 std::cout << "Time remaining: " << seconds << " seconds.                 \r" << std::flush;
161             }
162             else if (seconds < 60*60)
163             {
164                 ss = std::cout.precision(2);
165                 std::cout << "Time remaining: " << seconds/60 << " minutes.                 \r" << std::flush;
166             }
167             else
168             {
169                 ss = std::cout.precision(2);
170                 std::cout << "Time remaining: " << seconds/60/60 << " hours.                 \r" << std::flush;
171             }
172 
173             // restore previous output flags and precision settings
174             std::cout.flags(oldflags);
175             std::cout.precision(ss);
176 
177             return true;
178         }
179 
180         return false;
181     }
182 
183 // ----------------------------------------------------------------------------------------
184 
185     double console_progress_indicator::
target()186     target (
187     ) const
188     {
189         return target_val;
190     }
191 
192 // ----------------------------------------------------------------------------------------
193 
194     void console_progress_indicator::
reset(double target_value)195     reset (
196         double target_value
197     )
198     {
199         *this = console_progress_indicator(target_value);
200     }
201 
202 // ----------------------------------------------------------------------------------------
203 
204 }
205 
206 #endif // DLIB_CONSOLE_PROGRESS_INDiCATOR_Hh_
207 
208