1 /**
2  * @file core/util/param_checks_impl.hpp
3  * @author Ryan Curtin
4  *
5  * Utility function implementation for checking arguments, and so forth.
6  *
7  * mlpack is free software; you may redistribute it and/or modify it under the
8  * terms of the 3-clause BSD license.  You should have received a copy of the
9  * 3-clause BSD license along with mlpack.  If not, see
10  * http://www.opensource.org/licenses/BSD-3-Clause for more information.
11  */
12 #ifndef MLPACK_CORE_UTIL_PARAM_CHECKS_IMPL_HPP
13 #define MLPACK_CORE_UTIL_PARAM_CHECKS_IMPL_HPP
14 
15 #include "param_checks.hpp"
16 
17 namespace mlpack {
18 namespace util {
19 
20 // Check that the arguments are given.
RequireOnlyOnePassed(const std::vector<std::string> & constraints,const bool fatal,const std::string & errorMessage)21 inline void RequireOnlyOnePassed(
22     const std::vector<std::string>& constraints,
23     const bool fatal,
24     const std::string& errorMessage)
25 {
26   if (BINDING_IGNORE_CHECK(constraints))
27     return;
28 
29   size_t set = 0;
30   for (size_t i = 0; i < constraints.size(); ++i)
31   {
32     if (IO::HasParam(constraints[i]))
33       ++set;
34   }
35 
36   util::PrefixedOutStream& stream = fatal ? Log::Fatal : Log::Warn;
37   if (set > 1)
38   {
39     // Give different output depending on whether 2 or more parameters are
40     // given.
41     if (constraints.size() == 2)
42     {
43       stream << "Can only pass one of " << PRINT_PARAM_STRING(constraints[0])
44           << " or " << PRINT_PARAM_STRING(constraints[1]);
45     }
46     else
47     {
48       stream << "Can only pass one of ";
49       for (size_t i = 0 ; i < constraints.size() - 1; ++i)
50         stream << PRINT_PARAM_STRING(constraints[i]) << ", ";
51       stream << "or "
52           << PRINT_PARAM_STRING(constraints[constraints.size() - 1]);
53     }
54 
55     // Append a custom message.
56     if (!errorMessage.empty())
57       stream << "; " << errorMessage;
58     stream << "!" << std::endl;
59   }
60   else if (set == 0)
61   {
62     stream << (fatal ? "Must " : "Should ");
63 
64     // Give different output depending on whether 1, 2, or more parameters are
65     // given.
66     if (constraints.size() == 1)
67     {
68       stream << "specify " << PRINT_PARAM_STRING(constraints[0]);
69     }
70     else if (constraints.size() == 2)
71     {
72       stream << "specify one of " << PRINT_PARAM_STRING(constraints[0])
73           << " or " << PRINT_PARAM_STRING(constraints[1]);
74     }
75     else
76     {
77       stream << "specify one of ";
78       for (size_t i = 0; i < constraints.size() - 1; ++i)
79         stream << PRINT_PARAM_STRING(constraints[i]) << ", ";
80       stream << "or "
81           << PRINT_PARAM_STRING(constraints[constraints.size() - 1]);
82     }
83 
84     // Append a custom message.
85     if (!errorMessage.empty())
86       stream << "; " << errorMessage;
87     stream << "!" << std::endl;
88   }
89 }
90 
RequireAtLeastOnePassed(const std::vector<std::string> & constraints,const bool fatal,const std::string & errorMessage)91 inline void RequireAtLeastOnePassed(
92     const std::vector<std::string>& constraints,
93     const bool fatal,
94     const std::string& errorMessage)
95 {
96   if (BINDING_IGNORE_CHECK(constraints))
97     return;
98 
99   size_t set = 0;
100   for (size_t i = 0; i < constraints.size(); ++i)
101   {
102     if (IO::HasParam(constraints[i]))
103       ++set;
104   }
105 
106   if (set == 0)
107   {
108     util::PrefixedOutStream& stream = fatal ? Log::Fatal : Log::Warn;
109     stream << (fatal ? "Must " : "Should ");
110     if (constraints.size() == 1)
111     {
112       // This shouldn't happen... just use PARAM_*_REQ()...
113       stream << "pass " << PRINT_PARAM_STRING(constraints[0]);
114     }
115     else if (constraints.size() == 2)
116     {
117       stream << "pass either " << PRINT_PARAM_STRING(constraints[0])
118           << " or " << PRINT_PARAM_STRING(constraints[1]) << " or both";
119     }
120     else
121     {
122       stream << "pass one of ";
123       for (size_t i = 0; i < constraints.size() - 1; ++i)
124         stream << PRINT_PARAM_STRING(constraints[i]) << ", ";
125       stream << "or "
126           << PRINT_PARAM_STRING(constraints[constraints.size() - 1]);
127     }
128 
129     // Append custom error message.
130     if (!errorMessage.empty())
131       stream << "; " << errorMessage << "!" << std::endl;
132     else
133       stream << "!" << std::endl;
134   }
135 }
136 
RequireNoneOrAllPassed(const std::vector<std::string> & constraints,const bool fatal,const std::string & errorMessage)137 inline void RequireNoneOrAllPassed(
138     const std::vector<std::string>& constraints,
139     const bool fatal,
140     const std::string& errorMessage)
141 {
142   if (BINDING_IGNORE_CHECK(constraints))
143     return;
144 
145   size_t set = 0;
146   for (size_t i = 0; i < constraints.size(); ++i)
147   {
148     if (IO::HasParam(constraints[i]))
149       ++set;
150   }
151 
152   if (set != 0 && set < constraints.size())
153   {
154     util::PrefixedOutStream& stream = fatal ? Log::Fatal : Log::Warn;
155     stream << (fatal ? "Must " : "Should ");
156     if (constraints.size() == 2)
157     {
158       stream << "pass none or both of " << PRINT_PARAM_STRING(constraints[0])
159           << " and " << PRINT_PARAM_STRING(constraints[1]);
160     }
161     else
162     {
163       // constraints.size() > 2.
164       stream << "pass none or all of ";
165       for (size_t i = 0; i < constraints.size() - 1; ++i)
166         stream << PRINT_PARAM_STRING(constraints[i]) << ", ";
167       stream << "and "
168           << PRINT_PARAM_STRING(constraints[constraints.size() - 1]);
169     }
170 
171     // Append custom error message.
172     if (!errorMessage.empty())
173       stream << "; " << errorMessage << "!" << std::endl;
174     else
175       stream << "!" << std::endl;
176   }
177 }
178 
179 template<typename T>
RequireParamInSet(const std::string & name,const std::vector<T> & set,const bool fatal,const std::string & errorMessage)180 void RequireParamInSet(const std::string& name,
181                             const std::vector<T>& set,
182                             const bool fatal,
183                             const std::string& errorMessage)
184 {
185   if (BINDING_IGNORE_CHECK(name))
186     return;
187 
188   if (std::find(set.begin(), set.end(), IO::GetParam<T>(name)) == set.end())
189   {
190     // The item was not found in the set.
191     util::PrefixedOutStream& stream = fatal ? Log::Fatal : Log::Warn;
192     stream << "Invalid value of " << PRINT_PARAM_STRING(name) << " specified ("
193         << PRINT_PARAM_VALUE(IO::GetParam<T>(name), true) << "); ";
194     if (!errorMessage.empty())
195       stream << errorMessage << "; ";
196     stream << "must be one of ";
197     for (size_t i = 0; i < set.size() - 1; ++i)
198       stream << PRINT_PARAM_VALUE(set[i], true) << ", ";
199     stream << "or " << PRINT_PARAM_VALUE(set[set.size() - 1], true) << "!"
200         << std::endl;
201   }
202 }
203 
204 template<typename T>
RequireParamValue(const std::string & name,const std::function<bool (T)> & conditional,const bool fatal,const std::string & errorMessage)205 void RequireParamValue(const std::string& name,
206                        const std::function<bool(T)>& conditional,
207                        const bool fatal,
208                        const std::string& errorMessage)
209 {
210   if (BINDING_IGNORE_CHECK(name))
211     return;
212 
213   // We need to make sure that the condition holds.
214   bool condition = conditional(IO::GetParam<T>(name));
215   if (!condition)
216   {
217     // The condition failed.
218     util::PrefixedOutStream& stream = fatal ? Log::Fatal : Log::Warn;
219     stream << "Invalid value of " << PRINT_PARAM_STRING(name) << " specified ("
220         << PRINT_PARAM_VALUE(IO::GetParam<T>(name), false) << "); "
221         << errorMessage << "!" << std::endl;
222   }
223 }
224 
ReportIgnoredParam(const std::vector<std::pair<std::string,bool>> & constraints,const std::string & paramName)225 inline void ReportIgnoredParam(
226     const std::vector<std::pair<std::string, bool>>& constraints,
227     const std::string& paramName)
228 {
229   if (BINDING_IGNORE_CHECK(paramName))
230     return;
231 
232   // Determine whether or not the condition is true.
233   bool condition = true;
234   for (size_t i = 0; i < constraints.size(); ++i)
235   {
236     if (IO::HasParam(constraints[i].first) != constraints[i].second)
237     {
238       condition = false;
239       break;
240     }
241   }
242 
243   // If the condition is satisfied, then report that the parameter is ignored
244   // (if the user passed it).
245   if (condition && IO::HasParam(paramName))
246   {
247     // The output will be different depending on whether there are 1, 2, or more
248     // constraints.
249     Log::Warn << PRINT_PARAM_STRING(paramName) << " ignored because ";
250     if (constraints.size() == 1)
251     {
252       Log::Warn << PRINT_PARAM_STRING(constraints[0].first)
253           << ((constraints[0].second) ? " is " : " is not ")
254           << "specified!" << std::endl;
255     }
256     else if (constraints.size() == 2)
257     {
258       if (constraints[0].second == constraints[1].second)
259       {
260         Log::Warn << ((constraints[0].second) ? "both " : "neither ")
261             << PRINT_PARAM_STRING(constraints[0].first)
262             << ((constraints[0].second) ? "or " : "nor ")
263             << PRINT_PARAM_STRING(constraints[1].first)
264             << " are specified!" << std::endl;
265       }
266       else
267       {
268         Log::Warn << PRINT_PARAM_STRING(constraints[0].first)
269             << ((constraints[0].second) ? " is " : " is not ")
270             << "specified and "
271             << ((constraints[1].second) ? " is " : " is not ")
272             << "specified!" << std::endl;
273       }
274     }
275     else
276     {
277       // List each constraint and whether or not it was or wasn't specified.
278       for (size_t i = 0; i < constraints.size(); ++i)
279       {
280         Log::Warn << PRINT_PARAM_STRING(constraints[i].first)
281             << ((constraints[i].second) ? " is " : " is not ")
282             << ((i == constraints.size() - 1) ? "specified!"
283                 : "specified and ");
284       }
285       Log::Warn << std::endl;
286     }
287   }
288 }
289 
ReportIgnoredParam(const std::string & paramName,const std::string & reason)290 inline void ReportIgnoredParam(const std::string& paramName,
291                                const std::string& reason)
292 {
293   // If the argument was passed, we need to print the reason.
294   if (IO::HasParam(paramName))
295   {
296     Log::Warn << PRINT_PARAM_STRING(paramName) << " ignored because "
297         << reason << "!" << std::endl;
298   }
299 }
300 
301 } // namespace util
302 } // namespace mlpack
303 
304 #endif
305