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             std::ostream& out = std::cout
71         );
72         /*!
73             ensures
74                 - print_status() assumes it is called with values which are linearly
75                   approaching target().  It will attempt to predict how much time is
76                   remaining until cur becomes equal to target().
77                 - prints a status message to out which indicates how much more time is
78                   left until cur is equal to target()
79                 - if (always_print) then
80                     - This function prints to the screen each time it is called.
81                 - else
82                     - This function throttles the printing so that at most 1 message is
83                       printed each second.  Note that it won't print anything to the screen
84                       until about one second has elapsed.  This means that the first call
85                       to print_status() never prints to the screen.
86                 - This function returns true if it prints to the screen and false
87                   otherwise.
88         !*/
89 
90     private:
91 
92         double target_val;
93 
94         time_t start_time;
95         double first_val;
96         double seen_first_val;
97         time_t last_time;
98 
99     };
100 
101 // ----------------------------------------------------------------------------------------
102 // ----------------------------------------------------------------------------------------
103 //                               IMPLEMENTATION DETAILS
104 // ----------------------------------------------------------------------------------------
105 // ----------------------------------------------------------------------------------------
106 
107     console_progress_indicator::
console_progress_indicator(double target_value)108     console_progress_indicator (
109         double target_value
110     ) :
111         target_val(target_value),
112         start_time(0),
113         first_val(0),
114         seen_first_val(false),
115         last_time(0)
116     {
117     }
118 
119 // ----------------------------------------------------------------------------------------
120 
121     bool console_progress_indicator::
print_status(double cur,bool always_print,std::ostream & out)122     print_status (
123         double cur,
124         bool always_print,
125         std::ostream& out
126     )
127     {
128         const time_t cur_time = std::time(0);
129 
130         // if this is the first time print_status has been called
131         // then collect some information and exit.  We will print status
132         // on the next call.
133         if (!seen_first_val)
134         {
135             start_time = cur_time;
136             last_time = cur_time;
137             first_val = cur;
138             seen_first_val = true;
139             return false;
140         }
141 
142         if (cur_time != last_time || always_print)
143         {
144             last_time = cur_time;
145             double delta_t = static_cast<double>(cur_time - start_time);
146             double delta_val = std::abs(cur - first_val);
147 
148             // don't do anything if cur is equal to first_val
149             if (delta_val < std::numeric_limits<double>::epsilon())
150                 return false;
151 
152             double seconds = delta_t/delta_val * std::abs(target_val - cur);
153 
154             std::ios::fmtflags oldflags = out.flags();
155 
156             out.setf(std::ios::fixed,std::ios::floatfield);
157             std::streamsize ss;
158 
159             if (seconds < 60)
160             {
161                 ss = out.precision(0);
162                 out << "Time remaining: " << seconds << " seconds.                 \r" << std::flush;
163             }
164             else if (seconds < 60*60)
165             {
166                 ss = out.precision(2);
167                 out << "Time remaining: " << seconds/60 << " minutes.                 \r" << std::flush;
168             }
169             else
170             {
171                 ss = out.precision(2);
172                 out << "Time remaining: " << seconds/60/60 << " hours.                 \r" << std::flush;
173             }
174 
175             // restore previous output flags and precision settings
176             out.flags(oldflags);
177             out.precision(ss);
178 
179             return true;
180         }
181 
182         return false;
183     }
184 
185 // ----------------------------------------------------------------------------------------
186 
187     double console_progress_indicator::
target()188     target (
189     ) const
190     {
191         return target_val;
192     }
193 
194 // ----------------------------------------------------------------------------------------
195 
196     void console_progress_indicator::
reset(double target_value)197     reset (
198         double target_value
199     )
200     {
201         *this = console_progress_indicator(target_value);
202     }
203 
204 // ----------------------------------------------------------------------------------------
205 
206 }
207 
208 #endif // DLIB_CONSOLE_PROGRESS_INDiCATOR_Hh_
209 
210