1 //
2 // Copyright (c) 2006-2017 Benjamin Kaufmann
3 //
4 // This file is part of Clasp. See http://www.cs.uni-potsdam.de/clasp/
5 //
6 // Permission is hereby granted, free of charge, to any person obtaining a copy
7 // of this software and associated documentation files (the "Software"), to
8 // deal in the Software without restriction, including without limitation the
9 // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 // sell copies of the Software, and to permit persons to whom the Software is
11 // furnished to do so, subject to the following conditions:
12 //
13 // The above copyright notice and this permission notice shall be included in
14 // all copies or substantial portions of the Software.
15 //
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22 // IN THE SOFTWARE.
23 //
24 #include <clasp/cli/clasp_options.h>
25 #include <clasp/minimize_constraint.h>
26 #include <clasp/lookahead.h>
27 #include <clasp/unfounded_check.h>
28 #include <potassco/program_opts/program_options.h>
29 #include <potassco/program_opts/typed_value.h>
30 #include <cstring>
31 #include <cfloat>
32 #include <fstream>
33 #include <cctype>
34 #ifdef _MSC_VER
35 #pragma warning (disable : 4996)
36 #endif
37 #if (defined(__cplusplus) && __cplusplus > 199711) || (defined(_MSC_VER) && _MSC_VER >= 1900)
38 #define CLASP_NOEXCEPT_X(X) noexcept(X)
39 #else
40 #define CLASP_NOEXCEPT_X(X)
41 #endif
42 /////////////////////////////////////////////////////////////////////////////////////////
43 // Helper MACROS
44 /////////////////////////////////////////////////////////////////////////////////////////
45 #define SET(x, v)           ( ((x)=(v)) == (v) )
46 #define SET_LEQ(x, v, m)    ( ((v)<=(m)) && SET((x), (v)) )
47 #define SET_GEQ(x, v, m)    ( ((v)>=(m)) && SET((x), (v)) )
48 #define SET_OR_FILL(x, v)   ( SET((x),(v)) || ((x) = 0, (x) = ~(x),true) )
49 #define SET_OR_ZERO(x,v)    ( SET((x),(v)) || SET((x),uint32(0)) )
50 #define SET_R(x, v, lo, hi) ( ((lo)<=(v)) && ((v)<=(hi)) && SET((x), (v)) )
51 /////////////////////////////////////////////////////////////////////////////////////////
52 // Primitive types/functions for string <-> T conversions
53 /////////////////////////////////////////////////////////////////////////////////////////
54 namespace bk_lib {
55 template <class T>
xconvert(const char * x,pod_vector<T> & out,const char ** errPos,int sep)56 static int xconvert(const char* x, pod_vector<T>& out, const char** errPos, int sep) {
57 	if (sep == 0) { sep = Potassco::def_sep; }
58 	typename pod_vector<T>::size_type sz = out.size();
59 	std::size_t t = Potassco::convert_seq<T>(x, out.max_size() - sz, std::back_inserter(out), static_cast<char>(sep), errPos);
60 	if (!t) { out.resize(sz); }
61 	return static_cast<int>(t);
62 }
63 template <class T>
xconvert(std::string & out,const pod_vector<T> & x)64 static std::string& xconvert(std::string& out, const pod_vector<T>& x) { return Potassco::xconvert(out, x.begin(), x.end()); }
65 }
66 namespace Potassco {
67 struct KV { const char* key; int value; };
68 static const struct OffType {} off = {};
xconvert(const char * x,const OffType &,const char ** errPos,int)69 static int xconvert(const char* x, const OffType&, const char** errPos, int) {
70 	bool temp = true;
71 	const char* n = x;
72 	if (xconvert(n, temp, &n, 0) && !temp) { x = n; }
73 	if (errPos) { *errPos = x; }
74 	return int(temp == false);
75 }
xconvert(std::string & out,const OffType &)76 static std::string& xconvert(std::string& out, const OffType&) { return out.append("no"); }
77 
findValue(const Span<KV> & map,const char * key,const char ** next,const char * sep=",")78 static const KV* findValue(const Span<KV>& map, const char* key, const char** next, const char* sep = ",") {
79 	std::size_t kLen = std::strcspn(key, sep);
80 	const KV* needle = 0;
81 	for (const KV* it = Potassco::begin(map), *end = Potassco::end(map); it != end; ++it) {
82 		if (strncasecmp(key, it->key, kLen) == 0 && !it->key[kLen]) {
83 			needle = it;
84 			key   += kLen;
85 			break;
86 		}
87 	}
88 	if (next) { *next = key; }
89 	return needle;
90 }
findKey(const Span<KV> & map,int x)91 static const char* findKey(const Span<KV>& map, int x) {
92 	for (const KV* it = Potassco::begin(map), *end = Potassco::end(map); it != end; ++it) {
93 		if (it->value == x) { return it->key; }
94 	}
95 	return "";
96 }
97 
98 struct ArgString {
ArgStringPotassco::ArgString99 	ArgString(const char* x) : in(x), skip(0) { }
CLASP_NOEXCEPT_XPotassco::ArgString100 	~ArgString() CLASP_NOEXCEPT_X(false) { POTASSCO_ASSERT(!ok() || !*in || off(), "Unused argument!"); }
okPotassco::ArgString101 	bool ok()       const { return in != 0; }
offPotassco::ArgString102 	bool off()      const { return ok() && stringTo(in, Potassco::off); }
emptyPotassco::ArgString103 	bool empty()    const { return ok() && !*in; }
operator void*Potassco::ArgString104 	operator void*()const { return (void*)in; }
peekPotassco::ArgString105 	char peek()     const { return ok() ? in[(*in == skip)] : 0; }
106 	template <class T>
getPotassco::ArgString107 	ArgString& get(T& x)  {
108 		if (ok()) {
109 			const char* next = in + (*in == skip);
110 			in = xconvert(next, x, &next, 0) != 0 ? next : 0;
111 			skip = ',';
112 		}
113 		return *this;
114 	}
115 	const char* in;
116 	char  skip;
117 	template <class T>
118 	struct Opt_t {
Opt_tPotassco::ArgString::Opt_t119 		Opt_t(T& x) : obj(&x) {}
120 		T* obj;
121 	};
122 };
123 template <class T>
opt(T & x)124 inline ArgString::Opt_t<T> opt(T& x) { return ArgString::Opt_t<T>(x); }
125 template <class T>
operator >>(ArgString & arg,T & x)126 inline ArgString& operator>>(ArgString& arg, T& x) { return arg.get(x); }
127 template <class T>
operator >>(ArgString & arg,const ArgString::Opt_t<T> & x)128 inline ArgString& operator>>(ArgString& arg, const ArgString::Opt_t<T>& x) { return !arg.empty() ? arg.get(*x.obj) : arg; }
129 
130 struct StringRef {
StringRefPotassco::StringRef131 	StringRef(std::string& o) : out(&o) {}
132 	std::string* out;
133 };
134 template <class T>
operator <<(StringRef & str,const T & val)135 inline StringRef& operator<<(StringRef& str, const T& val) {
136 	if (!str.out->empty()) { str.out->append(1, ','); }
137 	xconvert(*str.out, val);
138 	return str;
139 }
140 
141 template <class ET>
142 struct Set {
SetPotassco::Set143 	Set(unsigned v = 0) : val(v) {}
valuePotassco::Set144 	unsigned value() const { return val; }
145 	unsigned val;
146 };
147 // <list_of_keys>|<bitmask>
148 template <class ET>
xconvert(const char * x,Set<ET> & out,const char ** errPos,int e)149 static int xconvert(const char* x, Set<ET>& out, const char** errPos, int e) {
150 	const char* it = x, *next;
151 	unsigned n, len = 0u; ET v;
152 	if (xconvert(it, n, &next, e)) {
153 		const Potassco::Span<Potassco::KV> em = enumMap(static_cast<ET*>(0));
154 		for (size_t i = 0, sum = 0; i != em.size && !len; ++i) {
155 			sum |= static_cast<unsigned>(em[i].value);
156 			len += (n == static_cast<unsigned>(em[i].value)) || (n && (n & sum) == n);
157 		}
158 	}
159 	else {
160 		for (next = "", n = 0u; xconvert(it + int(*next == ','), v, &next, e); it = next, ++len) {
161 			n |= static_cast<unsigned>(v);
162 		}
163 	}
164 	if (len)    { out.val = n; it = next; }
165 	if (errPos) { *errPos = it; }
166 	return static_cast<int>(len);
167 }
168 template <class ET>
xconvert(std::string & out,const Set<ET> & x)169 static std::string& xconvert(std::string& out, const Set<ET>& x) {
170 	const Potassco::Span<Potassco::KV> em = enumMap(static_cast<ET*>(0));
171 	if (unsigned bitset = x.val) {
172 		for (const KV* k = Potassco::begin(em), *kEnd = Potassco::end(em); k != kEnd; ++k) {
173 			unsigned ev = static_cast<unsigned>(k->value);
174 			if (bitset == ev || (ev && (ev & bitset) == ev)) {
175 				out.append(k->key);
176 				if ((bitset -= ev) == 0u) { return out; }
177 				out.append(1, ',');
178 			}
179 		}
180 		return xconvert(out, static_cast<ET>(bitset));
181 	}
182 	return xconvert(out, off);
183 }
184 
185 }
186 namespace Clasp {
187 /////////////////////////////////////////////////////////////////////////////////////////
188 // Enum mappings for clasp types
189 /////////////////////////////////////////////////////////////////////////////////////////
190 #define MAP(x, y) {static_cast<const char*>(x), static_cast<int>(y)}
191 #define DEFINE_ENUM_MAPPING(X, ...) \
192 static Potassco::Span<Potassco::KV> enumMap(const X*) {\
193 	static const Potassco::KV map[] = {__VA_ARGS__};\
194 	return Potassco::toSpan(map, sizeof(map)/sizeof(map[0]));\
195 }\
196 static int xconvert(const char* x, X& out, const char** errPos, int) {\
197 	if (const Potassco::KV* it = Potassco::findValue(enumMap(&out), x, errPos)) { \
198 		out = static_cast<X>(it->value); \
199 		return 1;\
200 	}\
201 	return 0;\
202 }\
203 static std::string& xconvert(std::string& out, X x) { \
204 	return out.append(Potassco::findKey(enumMap(&x), static_cast<int>(x))); \
205 }
206 #define OPTION(k, e, a, d, ...) a
207 #define CLASP_CONTEXT_OPTIONS
208 #define CLASP_GLOBAL_OPTIONS
209 #define CLASP_SOLVE_OPTIONS
210 #define CLASP_ASP_OPTIONS
211 #define CLASP_SOLVER_OPTIONS
212 #define CLASP_SEARCH_OPTIONS
213 #define ARG_EXT(a, X) X
214 #define ARG(a)
215 #define NO_ARG
216 #include <clasp/cli/clasp_cli_options.inl>
217 namespace Cli {
218 DEFINE_ENUM_MAPPING(ConfigKey, \
219   MAP("auto",   config_default), MAP("frumpy", config_frumpy), MAP("jumpy",  config_jumpy), \
220   MAP("tweety", config_tweety) , MAP("handy" , config_handy) ,\
221   MAP("crafty", config_crafty) , MAP("trendy", config_trendy), MAP("many", config_many))
222 }
223 #undef MAP
224 #undef DEFINE_ENUM_MAPPING
225 /////////////////////////////////////////////////////////////////////////////////////////
226 // Conversion functions for complex clasp types
227 /////////////////////////////////////////////////////////////////////////////////////////
xconvert(const char * x,ScheduleStrategy & out,const char ** errPos,int e)228 static int xconvert(const char* x, ScheduleStrategy& out, const char** errPos, int e) {
229 	using Potassco::xconvert;
230 	if (!x) { return 0; }
231 	const char* next = std::strchr(x, ',');
232 	uint32      base = 0;
233 	int         tok  = 1;
234 	if (errPos) { *errPos = x; }
235 	if (!next || !xconvert(next+1, base, &next, e) || base == 0)         { return 0; }
236 	if (strncasecmp(x, "f,", 2) == 0 || strncasecmp(x, "fixed,", 6) == 0){
237 		out = ScheduleStrategy::fixed(base);
238 	}
239 	else if (strncasecmp(x, "l,", 2) == 0 || strncasecmp(x, "luby,", 5) == 0) {
240 		uint32 lim = 0;
241 		if (*next == ',' && !xconvert(next+1, lim, &next, e)) { return 0; }
242 		out = ScheduleStrategy::luby(base, lim);
243 	}
244 	else if (strncmp(x, "+,", 2) == 0 || strncasecmp(x, "add,", 4) == 0) {
245 		std::pair<uint32, uint32> arg(0, 0);
246 		if (*next != ',' || !xconvert(next+1, arg, &next, e)) { return 0; }
247 		out = ScheduleStrategy::arith(base, arg.first, arg.second);
248 	}
249 	else if (strncmp(x, "x,", 2) == 0 || strncmp(x, "*,", 2) == 0 || strncasecmp(x, "d,", 2) == 0) {
250 		std::pair<double, uint32> arg(0, 0);
251 		if (*next != ',' || !xconvert(next+1, arg, &next, e)) { return 0; }
252 		if      (strncasecmp(x, "d", 1) == 0 && arg.first > 0.0) { out = ScheduleStrategy(ScheduleStrategy::User, base, arg.first, arg.second); }
253 		else if (strncasecmp(x, "d", 1) != 0 && arg.first >= 1.0){ out = ScheduleStrategy::geom(base, arg.first, arg.second); }
254 		else { return 0; }
255 	}
256 	else { next = x; tok = 0; }
257 	if (errPos) { *errPos = next; }
258 	return tok;
259 }
xconvert(std::string & out,const ScheduleStrategy & sched)260 static std::string& xconvert(std::string& out, const ScheduleStrategy& sched) {
261 	using Potassco::xconvert;
262 	if (sched.defaulted()){ return xconvert(out, ScheduleStrategy()); }
263 	if (sched.disabled()) { return out.append("0"); }
264 	std::size_t t = out.size();
265 	out.append("f,");
266 	xconvert(out, sched.base);
267 	switch (sched.type) {
268 		case ScheduleStrategy::Geometric:
269 			out[t] = 'x';
270 			return xconvert(out.append(1, ','), std::make_pair((double)sched.grow, sched.len));
271 		case ScheduleStrategy::Arithmetic:
272 			if (sched.grow) { out[t] = '+'; return xconvert(out.append(1, ','), std::make_pair((uint32)sched.grow, sched.len)); }
273 			else            { out[t] = 'f'; return out; }
274 		case ScheduleStrategy::Luby:
275 			out[t] = 'l';
276 			if (sched.len) { return xconvert(out.append(1, ','), sched.len); }
277 			else           { return out; }
278 		case ScheduleStrategy::User:
279 			out[t] = 'd';
280 			return xconvert(out.append(1, ','), std::make_pair((double)sched.grow, sched.len));
281 		default: POTASSCO_ASSERT(false, "xconvert(ScheduleStrategy): unknown type");
282 	}
283 }
setOptLegacy(OptParams & out,uint32 n)284 static bool setOptLegacy(OptParams& out, uint32 n) {
285 	if (n >= 20) { return false; }
286 	out.type = n < 4  ? OptParams::type_bb : OptParams::type_usc;
287 	out.algo = n < 4  ? n : 0;
288 	out.opts = 0u;
289 	out.kLim = 0u;
290 	if (n > 3 && (n -= 4u) != 0u) {
291 		if (test_bit(n, 0)) { out.opts |= OptParams::usc_disjoint; }
292 		if (test_bit(n, 1)) { out.opts |= OptParams::usc_succinct; }
293 		if (test_bit(n, 2)) { out.algo = OptParams::usc_pmr; }
294 		if (test_bit(n, 3)) { out.opts |= OptParams::usc_stratify; }
295 	}
296 	return true;
297 }
xconvert(const char * x,OptParams & out,const char ** err,int e)298 static int xconvert(const char* x, OptParams& out, const char** err, int e) {
299 	using Potassco::xconvert;
300 	using Potassco::toString;
301 	const char* it = x, *next;
302 	unsigned n = 0u, len = 0u;
303 	OptParams::Type t;
304 	// clasp-3.0: <n>
305 	if (xconvert(it, n, &next, e) && setOptLegacy(out, n)) {
306 		it = next; ++len;
307 	}
308 	else if (xconvert(it, t, &next, e)) {
309 		setOptLegacy(out, uint32(t)*4);
310 		it = next; ++len;
311 		if (*it == ',') {
312 			union { OptParams::BBAlgo bb; OptParams::UscAlgo usc; } algo;
313 			if (xconvert(it+1, n, &next, e) && setOptLegacy(out, n + (uint32(t)*4))) { // clasp-3.2: (bb|usc),<n>
314 				it = next; ++len;
315 			}
316 			else if (t == OptParams::type_bb && xconvert(it+1, algo.bb, &next, e)) {
317 				out.algo = algo.bb;
318 				it = next; ++len;
319 			}
320 			else if (t == OptParams::type_usc) {
321 				Potassco::Set<OptParams::UscOption> opts(0);
322 				if (xconvert(it+1, algo.usc, &next, e)) {
323 					out.algo = algo.usc;
324 					it = next; ++len;
325 					if (*it == ',' && algo.usc == OptParams::usc_k && xconvert(it + 1, n, &next)) {
326 						SET_OR_FILL(out.kLim, n);
327 						it = next; ++len;
328 					}
329 				}
330 				if (*it == ',' && (xconvert(it + 1, Potassco::off, &next, e) || xconvert(it + 1, opts, &next, e))) {
331 					out.opts = opts.value();
332 					it = next; ++len;
333 				}
334 			}
335 		}
336 	}
337 	if (err) { *err = it; }
338 	return static_cast<int>(len);
339 }
xconvert(std::string & out,const OptParams & p)340 static std::string& xconvert(std::string& out, const OptParams& p) {
341 	xconvert(out, static_cast<OptParams::Type>(p.type));
342 	if (p.type == OptParams::type_usc) {
343 		xconvert(out.append(1, ','), static_cast<OptParams::UscAlgo>(p.algo));
344 		if (p.algo == OptParams::usc_k ) { Potassco::xconvert(out.append(1, ','), p.kLim); }
345 		if (p.opts) { Potassco::xconvert(out.append(1, ','), Potassco::Set<OptParams::UscOption>(p.opts)); }
346 	}
347 	else {
348 		xconvert(out.append(1, ','), static_cast<OptParams::BBAlgo>(p.algo));
349 	}
350 	return out;
351 }
xconvert(const char * x,SatPreParams & out,const char ** err,int e)352 static int xconvert(const char* x, SatPreParams& out, const char** err, int e) {
353 	using Potassco::xconvert;
354 	if (xconvert(x, Potassco::off, err, e)) {
355 		out = SatPreParams();
356 		return 1;
357 	}
358 	uint32 n, len = 0;
359 	const char *next;
360 	if (xconvert(x, n, &next, e) && SET(out.type, n)) {
361 		x = next; ++len;
362 		Potassco::KV kv[5] = {{"iter", 0}, {"occ", 0}, {"time", 0}, {"frozen", 0}, {"size", 4000}};
363 		Potassco::Span<Potassco::KV> map = Potassco::toSpan(kv, 5);
364 		for (uint32 id = 0; *x == ','; ++id, ++len) {
365 			const char* it = x;
366 			if (const Potassco::KV* val = Potassco::findValue(map, it + 1, &next, ":=")) {
367 				id = static_cast<uint32>(val - kv);
368 				it = next;
369 			}
370 			if (id > 4 || !xconvert(it + 1, kv[id].value, &next, e)) { break; }
371 			x = next;
372 		}
373 		SET_OR_ZERO(out.limIters,  unsigned(kv[0].value));
374 		SET_OR_ZERO(out.limOcc,    unsigned(kv[1].value));
375 		SET_OR_ZERO(out.limTime,   unsigned(kv[2].value));
376 		SET_OR_ZERO(out.limFrozen, unsigned(kv[3].value));
377 		SET_OR_ZERO(out.limClause, unsigned(kv[4].value));
378 	}
379 	if (err) { *err = x; }
380 	return static_cast<int>(len);
381 }
xconvert(std::string & out,const SatPreParams & p)382 static std::string& xconvert(std::string& out, const SatPreParams& p) {
383 	if (p.type) {
384 		Potassco::xconvert(out, p.type);
385 		if (uint32 n = p.limIters)  { Potassco::xconvert(out.append(",iter="), n);   }
386 		if (uint32 n = p.limOcc)    { Potassco::xconvert(out.append(",occ="), n);    }
387 		if (uint32 n = p.limTime)   { Potassco::xconvert(out.append(",time="), n);   }
388 		if (uint32 n = p.limFrozen) { Potassco::xconvert(out.append(",frozen="), n); }
389 		if (uint32 n = p.limClause) { Potassco::xconvert(out.append(",size="), n); }
390 		return out;
391 	}
392 	else {
393 		return xconvert(out, Potassco::off);
394 	}
395 }
396 namespace Asp { using Clasp::xconvert; }
397 namespace mt  { using Clasp::xconvert; }
398 namespace Cli {
399 /////////////////////////////////////////////////////////////////////////////////////////
400 // Option -> Key mapping
401 /////////////////////////////////////////////////////////////////////////////////////////
402 /// \cond
403 // Valid option keys.
404 enum OptionKey {
405 	detail__before_options = -1,
406 	meta_config = 0,
407 #define CLASP_CONTEXT_OPTIONS  GRP(option_category_nodes_end,   option_category_context_begin),
408 #define CLASP_GLOBAL_OPTIONS   GRP(option_category_context_end, option_category_global_begin),
409 #define CLASP_SOLVER_OPTIONS   GRP(option_category_global_end,  option_category_solver_begin),
410 #define CLASP_SEARCH_OPTIONS   GRP(option_category_solver_end,  option_category_search_begin),
411 #define CLASP_ASP_OPTIONS      GRP(option_category_search_end,  option_category_asp_begin),
412 #define CLASP_SOLVE_OPTIONS    GRP(option_category_asp_end,     option_category_solve_begin),
413 #define OPTION(k,e,...) opt_##k,
414 #define GROUP_BEGIN(X) X
415 #define GRP(X, Y) X, Y = X, detail__before_##Y = X - 1
416 #include <clasp/cli/clasp_cli_options.inl>
417 #undef GRP
418 	option_category_solve_end,
419 	detail__num_options = option_category_solve_end,
420 	meta_tester = detail__num_options
421 };
isOption(int k)422 static inline bool isOption(int k)       { return k >= option_category_nodes_end && k < detail__num_options; }
isGlobalOption(int k)423 static inline bool isGlobalOption(int k) { return k >= option_category_global_begin && k < option_category_global_end; }
isTesterOption(int k)424 static inline bool isTesterOption(int k) { return k >= option_category_nodes_end && k < option_category_search_end && !isGlobalOption(k); }
isSolverOption(int k)425 static inline bool isSolverOption(int k) { return k >= option_category_solver_begin && k < option_category_search_end; }
426 #if CLASP_HAS_THREADS
427 #define MANY_DESC  "        many  : Use default portfolio to configure solver(s)\n"
428 #define MANY_ARG   "|many"
429 #else
430 #define MANY_DESC
431 #define MANY_ARG   ""
432 #endif
433 #define KEY_INIT_DESC(desc) \
434 desc "      <arg>: {auto|frumpy|jumpy|tweety|handy|crafty|trendy" MANY_ARG "|<file>}\n" \
435 "        auto  : Select configuration based on problem type\n"                          \
436 "        frumpy: Use conservative defaults\n"                                           \
437 "        jumpy : Use aggressive defaults\n"                                             \
438 "        tweety: Use defaults geared towards asp problems\n"                            \
439 "        handy : Use defaults geared towards large problems\n"                          \
440 "        crafty: Use defaults geared towards crafted problems\n"                        \
441 "        trendy: Use defaults geared towards industrial problems\n"                     \
442          MANY_DESC                                                                      \
443 "        <file>: Use configuration file to configure solver(s)"
444 
445 struct NodeKey {
446 	const char* name;
447 	const char* desc;
448 	int16       skBegin;
449 	int16       skEnd;
numSubkeysClasp::Cli::NodeKey450 	uint32      numSubkeys() const { return static_cast<uint32>( skEnd - skBegin ); }
451 };
452 enum { key_leaf = 0, key_solver = -1, key_asp = -2, key_solve = -3, key_tester = -4, key_root = -5 };
453 // nodes_g[-k]: entry for key k
454 static const NodeKey nodes_g[] = {
455 /* 0: config */ {"configuration", KEY_INIT_DESC("Initializes this configuration\n"), 0,0},
456 /* 1: */ {"solver", "Solver Options", option_category_solver_begin, option_category_search_end},
457 /* 2: */ {"asp"   , "Asp Options"   , option_category_asp_begin, option_category_asp_end},
458 /* 3: */ {"solve" , "Solve Options" , option_category_solve_begin, option_category_solve_end},
459 /* 4: */ {"tester", "Tester Options", key_solver, option_category_context_end},
460 /* 5: */ {""      , "Options"       , key_tester, option_category_global_end}
461 };
makeKeyHandle(int16 kId,uint32 mode,uint32 sId)462 static uint32 makeKeyHandle(int16 kId, uint32 mode, uint32 sId) {
463 	assert(sId <= 255 && mode <= 255);
464 	return (mode << 24) | (sId << 16) | static_cast<uint16>(kId);
465 }
decodeKey(uint32 key)466 static int16 decodeKey(uint32 key)   { return static_cast<int16>(static_cast<uint16>(key)); }
decodeMode(uint32 key)467 static uint8 decodeMode(uint32 key)  { return static_cast<uint8>( (key >> 24) ); }
decodeSolver(uint32 key)468 static uint8 decodeSolver(uint32 key){ return static_cast<uint8>( (key >> 16) ); }
isValidId(int16 id)469 static bool  isValidId(int16 id)     { return id >= key_root && id < detail__num_options; }
isLeafId(int16 id)470 static bool  isLeafId(int16 id)      { return id >= key_leaf && id < detail__num_options; }
471 const ClaspCliConfig::KeyType ClaspCliConfig::KEY_INVALID = static_cast<ClaspCliConfig::KeyType>(-1);
472 const ClaspCliConfig::KeyType ClaspCliConfig::KEY_ROOT    = makeKeyHandle(key_root, 0, 0);
473 const ClaspCliConfig::KeyType ClaspCliConfig::KEY_SOLVER  = makeKeyHandle(key_solver, 0, 0);
474 const ClaspCliConfig::KeyType ClaspCliConfig::KEY_TESTER  = makeKeyHandle(key_tester, ClaspCliConfig::mode_tester, 0);
475 
476 struct Name2Id {
477 	const char* name; int key;
operator <Clasp::Cli::Name2Id478 	bool operator<(const Name2Id& rhs) const { return std::strcmp(name, rhs.name) < 0; }
479 };
480 static Name2Id options_g[detail__num_options+1] = {
481 	{"configuration", meta_config},
482 #define OPTION(k, e, ...) { #k, opt_##k },
483 #define CLASP_CONTEXT_OPTIONS
484 #define CLASP_GLOBAL_OPTIONS
485 #define CLASP_SOLVER_OPTIONS
486 #define CLASP_SEARCH_OPTIONS
487 #define CLASP_ASP_OPTIONS
488 #define CLASP_SOLVE_OPTIONS
489 #include <clasp/cli/clasp_cli_options.inl>
490 	{"tester"       , meta_tester}
491 };
492 struct ClaspCliConfig::OptIndex {
OptIndexClasp::Cli::ClaspCliConfig::OptIndex493 	OptIndex(Name2Id* first, Name2Id* last) {
494 		std::sort(begin = first, end = last);
495 	}
findClasp::Cli::ClaspCliConfig::OptIndex496 	Name2Id* find(const char* name) const {
497 		Name2Id temp = { name, 0 };
498 		Name2Id* it  = std::lower_bound(begin, end, temp);
499 		if (it != end && std::strcmp(it->name, name) == 0) { return it; }
500 		return 0;
501 	}
502 	Name2Id* begin;
503 	Name2Id* end;
504 };
505 ClaspCliConfig::OptIndex ClaspCliConfig::index_g(options_g, options_g + detail__num_options+1);
506 /// \endcond
507 /////////////////////////////////////////////////////////////////////////////////////////
508 // Interface to ProgramOptions
509 /////////////////////////////////////////////////////////////////////////////////////////
510 // Converts option key to command-line option name.
keyToCliName(std::string & out,const char * n,const char * ext)511 static void keyToCliName(std::string& out, const char* n, const char* ext) {
512 	out.clear();
513 	for (const char* x; (x = std::strchr(n, '_')) != 0; n = ++x) {
514 		out.append(n, x-n);
515 		out.append(1, '-');
516 	}
517 	out.append(n).append(ext);
518 }
519 // Converts command-line option name to option key.
cliNameToKey(std::string & out,const char * n)520 static void cliNameToKey(std::string& out, const char* n) {
521 	out.clear();
522 	for (const char* x; (x = std::strchr(n, '-')) != 0; n = ++x) {
523 		out.append(n, x-n);
524 		out.append(1, '_');
525 	}
526 	out.append(n);
527 }
528 // Type for storing one command-line option.
529 class ClaspCliConfig::ProgOption : public Potassco::ProgramOptions::Value {
530 public:
ProgOption(ClaspCliConfig & c,int o)531 	ProgOption(ClaspCliConfig& c, int o) : Potassco::ProgramOptions::Value(0), config_(&c), option_(o) {}
doParse(const std::string & opt,const std::string & value)532 	bool doParse(const std::string& opt, const std::string& value) {
533 		int ret = isOption(option_) ? config_->setActive(option_, value.c_str()) : config_->setAppOpt(option_, value.c_str());
534 		if (ret == -1) { throw Potassco::ProgramOptions::UnknownOption(config_->isGenerator() ? "<clasp>" : "<tester>", opt); }
535 		return ret > 0;
536 	}
option() const537 	int option() const { return option_; }
538 private:
539 	ClaspCliConfig* config_;
540 	int             option_;
541 };
542 
543 // Adapter for parsing a command string.
544 struct ClaspCliConfig::ParseContext : public Potassco::ProgramOptions::ParseContext{
545 	typedef Potassco::ProgramOptions::SharedOptPtr OptPtr;
ParseContextClasp::Cli::ClaspCliConfig::ParseContext546 	ParseContext(ClaspCliConfig& x, const char* c, const ParsedOpts* ex, bool allowMeta, ParsedOpts* o)
547 		: self(&x), config(c), exclude(ex), out(o), meta(allowMeta) { seen[0] = seen[1] = 0;  }
548 	OptPtr getOption(const char* name, FindType ft);
getOptionClasp::Cli::ClaspCliConfig::ParseContext549 	OptPtr getOption(int, const char* key) { throw Potassco::ProgramOptions::UnknownOption(config, key); }
550 	void   addValue(const OptPtr& key, const std::string& value);
551 	uint64            seen[2];
552 	std::string       temp;
553 	ClaspCliConfig*   self;
554 	const char*       config;
555 	const ParsedOpts* exclude;
556 	ParsedOpts*       out;
557 	bool              meta;
558 };
addValue(const OptPtr & key,const std::string & value)559 void ClaspCliConfig::ParseContext::addValue(const OptPtr& key, const std::string& value) {
560 	using namespace Potassco::ProgramOptions;
561 	if (exclude->count(key->name()) == 0) {
562 		ProgOption* v = static_cast<ProgOption*>(key->value());
563 		Value::State s= v->state();
564 		int        id = v->option();
565 		uint64&    xs = seen[id/64];
566 		uint64      m = static_cast<uint64>(1u) << (id & 63);
567 		if ((xs & m) != 0 && !v->isComposing()){ throw ValueError(config, ValueError::multiple_occurrences, key->name(), value); }
568 		if (!v->parse(key->name(), value, s))  { throw ValueError(config, ValueError::invalid_value, key->name(), value); }
569 		if (out) { out->add(key->name()); }
570 		xs |= m;
571 	}
572 }
getOption(const char * cmdName,FindType ft)573 Potassco::ProgramOptions::SharedOptPtr ClaspCliConfig::ParseContext::getOption(const char* cmdName, FindType ft) {
574 	Options::option_iterator end = self->opts_->end(), it = end;
575 	if (ft == OptionContext::find_alias) {
576 		char a = cmdName[*cmdName == '-'];
577 		for (it = self->opts_->begin(); it != end && it->get()->alias() != a; ++it) { ; }
578 	}
579 	else {
580 		Name2Id  key = { cmdName, -2 };
581 		if (std::strchr(cmdName, '-') != 0) { cliNameToKey(temp, cmdName); key.name = temp.c_str(); }
582 		Name2Id* pos = std::lower_bound(self->index_g.begin, self->index_g.end, key);
583 		if (pos != self->index_g.end) {
584 			std::size_t len = std::strlen(key.name);
585 			int  cmp        = std::strncmp(key.name, pos->name, len);
586 			bool found      = cmp == 0 && !*(pos->name+len);
587 			if (!found && cmp == 0 && (ft & OptionContext::find_prefix) != 0) {
588 				Name2Id* next = pos + 1;
589 				cmp           = next != self->index_g.end ? std::strncmp(key.name, next->name, len) : -1;
590 				found         = cmp != 0;
591 				if (!found) { throw Potassco::ProgramOptions::AmbiguousOption(config, cmdName, ""); }
592 			}
593 			if (found) { it = self->opts_->begin() + pos->key; }
594 		}
595 		assert(it == end || static_cast<const ProgOption*>(it->get()->value())->option() == pos->key);
596 	}
597 	if (it != end && (meta || isOption(static_cast<const ProgOption*>(it->get()->value())->option()))) {
598 		return *it;
599 	}
600 	throw Potassco::ProgramOptions::UnknownOption(config, cmdName);
601 }
602 /////////////////////////////////////////////////////////////////////////////////////////
603 // Default Configs
604 /////////////////////////////////////////////////////////////////////////////////////////
getConfig(ConfigKey k)605 ConfigIter ClaspCliConfig::getConfig(ConfigKey k) {
606 	switch(k) {
607 		#define CONFIG(id, n,c,s,p) case config_##n: return ConfigIter("/[" #n "]\0/\0/" s " " c "\0");
608 		#define CLASP_CLI_DEFAULT_CONFIGS
609 		#define CLASP_CLI_AUX_CONFIGS
610 		#include <clasp/cli/clasp_cli_configs.inl>
611 		case config_many:
612 		#define CONFIG(id,n,c,s,p) "/[solver." #id "]\0/\0/" c " " p "\0"
613 		#define CLASP_CLI_DEFAULT_CONFIGS
614 		#define CLASP_CLI_AUX_CONFIGS
615 			return ConfigIter(
616 				#include <clasp/cli/clasp_cli_configs.inl>
617 				);
618 		default: POTASSCO_REQUIRE(k == config_default, "Invalid config key '%d'", (int)k); return ConfigIter("/default\0/\0/\0");
619 	}
620 }
getConfig(uint8 key,std::string & tempMem)621 ConfigIter ClaspCliConfig::getConfig(uint8 key, std::string& tempMem) {
622 	POTASSCO_REQUIRE(key <= (config_max_value + 1), "Invalid key!");
623 	if (key < config_max_value) { return getConfig(static_cast<ConfigKey>(key)); }
624 	tempMem.clear();
625 	loadConfig(tempMem, config_[key - config_max_value].c_str());
626 	return ConfigIter(tempMem.data());
627 }
getConfigKey(const char * k)628 int ClaspCliConfig::getConfigKey(const char* k) {
629 	ConfigKey ret;
630 	return Potassco::string_cast(k, ret) ? ret : -1;
631 }
skipWs(const char * x)632 static inline const char* skipWs(const char* x) {
633 	while (*x == ' ' || *x == '\t') { ++x; }
634 	return x;
635 }
getIdent(const char * x,std::string & to)636 static inline const char* getIdent(const char* x, std::string& to) {
637 	for (x = skipWs(x); std::strchr(" \t:()[]", *x) == 0; ++x) { to += *x; }
638 	return x;
639 }
matchSep(const char * & x,char c)640 static inline bool matchSep(const char*& x, char c) {
641 	if (*(x = skipWs(x)) == c) { ++x; return true; }
642 	return false;
643 }
644 
appendConfig(std::string & to,const std::string & line)645 bool ClaspCliConfig::appendConfig(std::string& to, const std::string& line) {
646 	std::size_t sz = to.size();
647 	const char*  x = skipWs(line.c_str());
648 	const bool   p = matchSep(x, '[');
649 	to.append("/[", 2);
650 	// match name in optional square brackets
651 	bool ok = matchSep(x = getIdent(x, to), ']') == p;
652 	to.append("]\0/", 3);
653 	// match optional base in parentheses followed by start of option list
654 	if (ok && (!matchSep(x, '(') || matchSep((x = getIdent(x, to)), ')')) && matchSep(x, ':')) {
655 		to.append("\0/", 2);
656 		to.append(skipWs(x));
657 		to.erase(to.find_last_not_of(" \t") + 1);
658 		to.append(1, '\0');
659 		return true;
660 	}
661 	to.resize(sz);
662 	return false;
663 }
loadConfig(std::string & to,const char * name)664 bool ClaspCliConfig::loadConfig(std::string& to, const char* name) {
665 	std::ifstream file(name);
666 	POTASSCO_EXPECT(file, "Could not open config file '%s'", name);
667 	uint32 lineNum= 0;
668 	for (std::string line, cont; std::getline(file, line); ) {
669 		++lineNum;
670 		line.erase(0, line.find_first_not_of(" \t"));
671 		if (line.empty() || line[0] == '#') { continue; }
672 		if (*line.rbegin() == '\\')         { *line.rbegin() = ' '; cont += line; continue; }
673 		if (!cont.empty()) { cont += line; cont.swap(line); cont.clear(); }
674 		POTASSCO_EXPECT(appendConfig(to, line), "'%s@%u': Invalid configuration", name, lineNum);
675 	}
676 	to.append(1, '\0');
677 	return true;
678 }
getDefaults(ProblemType t)679 const char* ClaspCliConfig::getDefaults(ProblemType t) {
680 	if (t == Problem_t::Asp){ return "--configuration=tweety"; }
681 	else                    { return "--configuration=trendy"; }
682 }
ConfigIter(const char * x)683 ConfigIter::ConfigIter(const char* x) : base_(x) {}
name() const684 const char* ConfigIter::name() const { return base_ + 1; }
base() const685 const char* ConfigIter::base() const { return base_ + std::strlen(base_) + 2; }
args() const686 const char* ConfigIter::args() const { const char* x = base(); return x + std::strlen(x) + 2; }
valid() const687 bool        ConfigIter::valid()const { return *base_ != 0; }
next()688 bool        ConfigIter::next()       {
689 	base_ = args();
690 	base_+= std::strlen(base_) + 1;
691 	return valid();
692 }
693 /////////////////////////////////////////////////////////////////////////////////////////
694 // ClaspCliConfig
695 /////////////////////////////////////////////////////////////////////////////////////////
ScopedSet(ClaspCliConfig & s,uint8 mode,uint32 sId)696 ClaspCliConfig::ScopedSet::ScopedSet(ClaspCliConfig& s, uint8 mode, uint32 sId) : self(&s) {
697 	if (sId) { mode |= mode_solver; }
698 	s.cliId   = static_cast<uint8>(sId);
699 	s.cliMode = mode;
700 }
~ScopedSet()701 ClaspCliConfig::ScopedSet::~ScopedSet() { self->cliId = self->cliMode = 0; }
RawConfig(const char * name)702 ClaspCliConfig::RawConfig::RawConfig(const char* name) {
703 	raw.append(1, '/').append(name ? name : "").append("\0/\0/", 4);
704 }
addArg(const char * arg)705 void ClaspCliConfig::RawConfig::addArg(const char* arg) {
706 	*raw.rbegin() = ' ';
707 	raw.append(arg ? arg : "").append(1, '\0');
708 }
addArg(const std::string & arg)709 void ClaspCliConfig::RawConfig::addArg(const std::string& arg) { addArg(arg.c_str()); }
ClaspCliConfig()710 ClaspCliConfig::ClaspCliConfig()  {
711 	initTester_ = true;
712 	static_assert(
713 		(option_category_context_begin< option_category_solver_begin) &&
714 		(option_category_solver_begin < option_category_search_begin) &&
715 		(option_category_search_begin < option_category_asp_begin)    &&
716 		(option_category_asp_begin    < option_category_solve_begin)  &&
717 		(option_category_solve_begin  < option_category_solve_end), "unexpected option order");
718 }
~ClaspCliConfig()719 ClaspCliConfig::~ClaspCliConfig() {}
reset()720 void ClaspCliConfig::reset() {
721 	config_[0] = config_[1] = "";
722 	initTester_ = true;
723 	ClaspConfig::reset();
724 }
725 
prepare(SharedContext & ctx)726 void ClaspCliConfig::prepare(SharedContext& ctx) {
727 	if (testerConfig()) {
728 		// Force init
729 		ClaspCliConfig::config("tester");
730 	}
731 	ClaspConfig::prepare(ctx);
732 }
config(const char * n)733 Configuration* ClaspCliConfig::config(const char* n) {
734 	if (n && std::strcmp(n, "tester") == 0) {
735 		if (!testerConfig() || (!testerConfig()->hasConfig && initTester_)) {
736 			setAppOpt(meta_tester, "--config=auto");
737 			initTester_ = false;
738 		}
739 		return testerConfig();
740 	}
741 	return ClaspConfig::config(n);
742 }
743 
createOption(int o)744 ClaspCliConfig::ProgOption* ClaspCliConfig::createOption(int o) {  return new ProgOption(*this, o); }
745 
createOptions()746 void ClaspCliConfig::createOptions() {
747 	if (opts_.get()) { return; }
748 	opts_ = new Options();
749 	using namespace Potassco::ProgramOptions;
750 	opts_->addOptions()("configuration", createOption(meta_config)->defaultsTo("auto")->state(Value::value_defaulted), KEY_INIT_DESC("Set default configuration [%D]\n"));
751 	std::string cmdName;
752 #define CLASP_CONTEXT_OPTIONS
753 #define CLASP_GLOBAL_OPTIONS
754 #define CLASP_SOLVE_OPTIONS
755 #define CLASP_ASP_OPTIONS
756 #define CLASP_SOLVER_OPTIONS
757 #define CLASP_SEARCH_OPTIONS
758 #define OPTION(k, e, a, d, ...) keyToCliName(cmdName, #k, e); opts_->addOptions()(cmdName.c_str(),static_cast<ProgOption*>( createOption(opt_##k)a ), d);
759 #define ARG(a) ->a
760 #define ARG_EXT(a, X) ARG(a)
761 #define NO_ARG
762 #include <clasp/cli/clasp_cli_options.inl>
763 	opts_->addOptions()("tester", createOption(meta_tester)->arg("<options>"), "Pass (quoted) string of %A to tester");
764 }
addOptions(OptionContext & root)765 void ClaspCliConfig::addOptions(OptionContext& root) {
766 	createOptions();
767 	using namespace Potassco::ProgramOptions;
768 	OptionGroup configOpts("Clasp.Config Options");
769 	OptionGroup ctxOpts("Clasp.Context Options", Potassco::ProgramOptions::desc_level_e1);
770 	OptionGroup solving("Clasp.Solving Options");
771 	OptionGroup aspOpts("Clasp.ASP Options", Potassco::ProgramOptions::desc_level_e1);
772 	OptionGroup search("Clasp.Search Options", Potassco::ProgramOptions::desc_level_e1);
773 	OptionGroup lookback("Clasp.Lookback Options", Potassco::ProgramOptions::desc_level_e1);
774 	configOpts.addOption(*opts_->begin());
775 	configOpts.addOption(*(opts_->end()-1));
776 	for (Options::option_iterator it = opts_->begin() + 1, end = opts_->end() - 1; it != end; ++it) {
777 		int oId = static_cast<ProgOption*>(it->get()->value())->option();
778 		if      (isGlobalOption(oId))               { configOpts.addOption(*it);}
779 		else if (oId < option_category_context_end) { ctxOpts.addOption(*it); }
780 		else if (oId < opt_no_lookback)             { search.addOption(*it); }
781 		else if (oId < option_category_solver_end)  { lookback.addOption(*it); }
782 		else if (oId < opt_restarts)                { search.addOption(*it); }
783 		else if (oId < option_category_search_end)  { lookback.addOption(*it); }
784 		else if (oId < option_category_asp_end)     { aspOpts.addOption(*it); }
785 		else                                        { solving.addOption(*it); }
786 	}
787 	root.add(configOpts).add(ctxOpts).add(aspOpts).add(solving).add(search).add(lookback);
788 	root.addAlias("number", root.find("models")); // remove on next version
789 	root.addAlias("opt-sat", root.find("parse-maxsat")); // remove on next version
790 }
assignDefaults(const Potassco::ProgramOptions::ParsedOptions & exclude)791 bool ClaspCliConfig::assignDefaults(const Potassco::ProgramOptions::ParsedOptions& exclude) {
792 	for (Options::option_iterator it = opts_->begin(), end = opts_->end(); it != end; ++it) {
793 		const Potassco::ProgramOptions::Option& o = **it;
794 		POTASSCO_REQUIRE(exclude.count(o.name()) != 0 || o.assignDefault(), "Option '%s': invalid default value '%s'\n", o.name().c_str(), o.value()->defaultsTo());
795 	}
796 	return true;
797 }
releaseOptions()798 void ClaspCliConfig::releaseOptions() {
799 	opts_ = 0;
800 }
match(const char * & path,const char * what) const801 bool ClaspCliConfig::match(const char*& path, const char* what) const {
802 	std::size_t wLen = std::strlen(what);
803 	if (strncmp(path, what, wLen) != 0 || (path[wLen] && path[wLen++] != '.')) {
804 		return false;
805 	}
806 	path += wLen;
807 	return true;
808 }
getKey(KeyType k,const char * path) const809 ClaspCliConfig::KeyType ClaspCliConfig::getKey(KeyType k, const char* path) const {
810 	int16 id = decodeKey(k);
811 	if (!isValidId(id) || !path || !*path || (*path == '.' && !*++path)) {
812 		return k;
813 	}
814 	if (isLeafId(id)){ return KEY_INVALID; }
815 	const NodeKey& x = nodes_g[-id];
816 	for (int16 sk = x.skBegin; sk != x.skEnd && sk < 0; ++sk) {
817 		NodeKey sub = nodes_g[-sk];
818 		if (match(path, sub.name)) {
819 			KeyType ret = makeKeyHandle(sk, (sk == key_tester ? mode_tester : 0) | decodeMode(k), 0);
820 			if (!*path) { return ret; }
821 			return getKey(ret, path);
822 		}
823 	}
824 	uint8 mode = decodeMode(k);
825 	if (id == key_solver) {
826 		uint32 solverId;
827 		if ((mode & mode_solver) == 0 && *path != '.' && Potassco::xconvert(path, solverId, &path, 0) == 1) {
828 			return getKey(makeKeyHandle(id, mode | mode_solver, std::min(solverId, (uint32)uint8(-1))), path);
829 		}
830 		mode |= mode_solver;
831 	}
832 	const Name2Id* opt = index_g.find(path);
833 	// remaining name must be a valid option in our subkey range
834 	if (!opt || opt->key < x.skBegin || opt->key >= x.skEnd) {
835 		return KEY_INVALID;
836 	}
837 	return makeKeyHandle(static_cast<int16>(opt->key), mode, decodeSolver(k));
838 }
839 
getArrKey(KeyType k,unsigned i) const840 ClaspCliConfig::KeyType ClaspCliConfig::getArrKey(KeyType k, unsigned i) const {
841 	int16 id = decodeKey(k);
842 	if (id != key_solver || (decodeMode(k) & mode_solver) != 0 || i >= solve.supportedSolvers()) { return KEY_INVALID; }
843 	return makeKeyHandle(id, decodeMode(k) | mode_solver, i);
844 }
getKeyInfo(KeyType k,int * nSubkeys,int * arrLen,const char ** help,int * nValues) const845 int ClaspCliConfig::getKeyInfo(KeyType k, int* nSubkeys, int* arrLen, const char** help, int* nValues) const {
846 	int16 id = decodeKey(k);
847 	int ret  = 0;
848 	if (!isValidId(id)){ return -1; }
849 	if (isLeafId(id)){
850 		if (nSubkeys && ++ret) { *nSubkeys = 0;  }
851 		if (arrLen && ++ret)   { *arrLen   = -1; }
852 		if (nValues && ++ret)  { *nValues  = static_cast<int>( (decodeMode(k) & mode_tester) == 0 || testerConfig() != 0 ); }
853 		if (help && ++ret)     { getActive(id, 0, help, 0); }
854 		return ret;
855 	}
856 	const NodeKey& x = nodes_g[-id];
857 	if (nSubkeys && ++ret) { *nSubkeys = x.numSubkeys(); }
858 	if (nValues && ++ret)  { *nValues = -1; }
859 	if (help && ++ret)     { *help = x.desc; }
860 	if (arrLen && ++ret)   {
861 		*arrLen = -1;
862 		if (id == key_solver && (decodeMode(k) & mode_solver) == 0) {
863 			const UserConfig* c = (decodeMode(k) & mode_tester) == 0 ? this : testerConfig();
864 			*arrLen = c ? (int)c->numSolver() : 0;
865 		}
866 	}
867 	return ret;
868 }
isLeafKey(KeyType k)869 bool ClaspCliConfig::isLeafKey(KeyType k) { return isLeafId(decodeKey(k)); }
getSubkey(KeyType k,uint32 i) const870 const char* ClaspCliConfig::getSubkey(KeyType k, uint32 i) const {
871 	int16 id = decodeKey(k);
872 	if (!isValidId(id) || isLeafId(id)) { return 0; }
873 	const NodeKey& nk = nodes_g[-id];
874 	if (i >= nk.numSubkeys()) { return 0; }
875 	int sk = nk.skBegin + static_cast<int16>(i);
876 	if (sk < key_leaf) { return nodes_g[-sk].name; }
877 	const char* opt = 0;
878 	getActive(sk, 0, 0, &opt);
879 	return opt;
880 }
getValue(KeyType key,std::string & out) const881 int ClaspCliConfig::getValue(KeyType key, std::string& out) const {
882 	int16 id = decodeKey(key);
883 	if (!isLeafId(id)) { return -1; }
884 	try {
885 		int ret = ScopedSet(const_cast<ClaspCliConfig&>(*this), decodeMode(key), decodeSolver(key))->getActive(id, &out, 0, 0);
886 		return ret > 0 ? static_cast<int>(out.length()) : ret;
887 	}
888 	catch (...) { return -2; }
889 }
getValue(KeyType key,char * buffer,std::size_t bufSize) const890 int ClaspCliConfig::getValue(KeyType key, char* buffer, std::size_t bufSize) const {
891 	std::string temp;
892 	int ret = getValue(key, temp);
893 	if (ret <= 0) { return ret; }
894 	if (buffer && bufSize) {
895 		std::size_t n = temp.length() >= bufSize ? bufSize - 1 : temp.length();
896 		std::memcpy(buffer, temp.c_str(), n * sizeof(char));
897 		buffer[n] = 0;
898 	}
899 	return static_cast<int>(temp.length());
900 }
getValue(const char * path) const901 std::string ClaspCliConfig::getValue(const char* path) const {
902 	std::string temp;
903 	POTASSCO_REQUIRE(getValue(getKey(KEY_ROOT, path), temp) >= 0, "Invalid key: '%s'", path);
904 	return temp;
905 }
hasValue(const char * path) const906 bool ClaspCliConfig::hasValue(const char* path) const {
907 	int nVals;
908 	return getKeyInfo(getKey(KEY_ROOT, path), 0, 0, 0, &nVals) == 1 && nVals > 0;
909 }
910 
setValue(KeyType key,const char * value)911 int ClaspCliConfig::setValue(KeyType key, const char* value) {
912 	int16 id = decodeKey(key);
913 	if (!isLeafId(id)) { return -1; }
914 	if ((decodeMode(key) & mode_tester) != 0) {
915 		addTesterConfig();
916 		initTester_ = false;
917 	}
918 	ScopedSet scope(*this, decodeMode(key), decodeSolver(key));
919 	try         { return setActive(id, value); }
920 	catch (...) { return -2; }
921 }
922 
setValue(const char * path,const char * value)923 bool ClaspCliConfig::setValue(const char* path, const char* value) {
924 	int ret = setValue(getKey(KEY_ROOT, path), value);
925 	POTASSCO_REQUIRE(ret >= 0, (ret == -1 ? "Invalid or incomplete key: '%s'" : "Value error in key: '%s'"), path);
926 	return ret != 0;
927 }
928 
applyActive(int o,const char * _val_,std::string * _val_out_,const char ** _desc_out_,const char ** _name_out_)929 int ClaspCliConfig::applyActive(int o, const char* _val_, std::string* _val_out_, const char** _desc_out_, const char** _name_out_) {
930 	UserConfig*    active = this->active();
931 	uint32         sId    = cliId;
932 	SolverOpts*    solver = 0;
933 	SearchOpts*    search = 0;
934 	ContextParams* ctxOpts= active;
935 	if (_name_out_) { *_name_out_ = 0; }
936 	if (_val_ || _val_out_) {
937 		if (!active || (active == testerConfig() && !isTesterOption(o)) || ((cliMode & mode_solver) != 0 && !isSolverOption(o))) {
938 			o = (cliMode & mode_relaxed) != 0 ? detail__before_options : detail__num_options;
939 		}
940 		else if (isSolverOption(o)) {
941 			solver = &active->addSolver(sId);
942 			search = &active->addSearch(sId);
943 		}
944 	}
945 	if (!isOption(o)) {
946 		return o == detail__before_options ? int(_val_ != 0) : -1;
947 	}
948 	// action & helper macros used in get/set
949 	using Potassco::xconvert; using Potassco::off; using Potassco::opt;
950 	using Potassco::stringTo; using Potassco::toString; using Potassco::Set;
951 	#define ITE(c, a, b)            (!!(c) ? (a) : (b))
952 	#define FUN(x)                  for (Potassco::ArgString x = _val_;;)
953 	#define STORE(obj)              { return stringTo((_val_), obj); }
954 	#define STORE_LEQ(x, y)         { unsigned __n; return stringTo(_val_, __n) && SET_LEQ(x, __n, y); }
955 	#define STORE_FLAG(x)           { bool __b; return stringTo(_val_, __b) && SET(x, static_cast<unsigned>(__b)); }
956 	#define STORE_OR_FILL(x)        { unsigned __n; return stringTo(_val_, __n) && SET_OR_FILL(x, __n); }
957 	#define STORE_U(E, x)           { E __e; return stringTo((_val_), __e) && SET(x, static_cast<unsigned>(__e));}
958 	#define GET_FUN(x)              Potassco::StringRef x(*_val_out_); if (!x.out);else
959 	#define GET(...)                *_val_out_ = toString( __VA_ARGS__ )
960 	#define GET_IF(c, ...)          *_val_out_ = ITE((c), toString(__VA_ARGS__), toString(off))
961 	switch(static_cast<OptionKey>(o)) {
962 		default: return -1;
963 		#define OPTION(k, e, a, d, x, v) \
964 		case opt_##k:\
965 			if (_name_out_){ *_name_out_ = #k ; }\
966 			if (_val_) try { x ; }catch(...) {return 0;}\
967 			if (_val_out_) { v ; }\
968 			if (_desc_out_){ *_desc_out_ = d; }\
969 			return 1;
970 		#define CLASP_CONTEXT_OPTIONS (*ctxOpts)
971 		#define CLASP_GLOBAL_OPTIONS (*this)
972 		#define CLASP_ASP_OPTIONS asp
973 		#define CLASP_SOLVE_OPTIONS solve
974 		#define CLASP_SOLVER_OPTIONS (*solver)
975 		#define CLASP_SEARCH_OPTIONS (*search)
976 		#define CLASP_SEARCH_RESTART_OPTIONS search->restart
977 		#define CLASP_SEARCH_REDUCE_OPTIONS search->reduce
978 		#include <clasp/cli/clasp_cli_options.inl>
979 	}
980 	#undef FUN
981 	#undef STORE
982 	#undef STORE_LEQ
983 	#undef STORE_FLAG
984 	#undef STORE_OR_FILL
985 	#undef GET_IF
986 	#undef GET
987 	#undef ITE
988 }
989 
setActive(int id,const char * setVal)990 int ClaspCliConfig::setActive(int id, const char* setVal) {
991 	if      (isOption(id))     { return applyActive(id, setVal ? setVal : "", 0, 0, 0); }
992 	else if (id == meta_config){
993 		int sz = setAppOpt(id, setVal);
994 		if (sz <= 0) { return 0; }
995 		std::string m;
996 		UserConfig* act = active();
997 		ConfigIter it  = getConfig(act->cliConfig, m);
998 		act->hasConfig = 0;
999 		cliMode       |= mode_relaxed;
1000 		act->resize(1, 1);
1001 		for (uint32 sId = 0; it.valid(); it.next()) {
1002 			act->addSolver(sId);
1003 			act->addSearch(sId);
1004 			cliId = static_cast<uint8>(sId);
1005 			if (!setConfig(it, false, ParsedOpts(), 0)){ return 0; }
1006 			if (++sId == static_cast<uint32>(sz))      { break; }
1007 			cliMode |= mode_solver;
1008 		}
1009 		if (sz < 65 && static_cast<uint32>(sz) > act->numSolver()) {
1010 			for (uint32 sId = act->numSolver(), mod = sId, end = static_cast<uint32>(sz); sId != end; ++sId) {
1011 				SolverParams& solver = act->addSolver(sId);
1012 				SolveParams&  search = act->addSearch(sId);
1013 				(solver = act->solver(sId % mod)).setId(sId);
1014 				search = act->search(sId % mod);
1015 			}
1016 		}
1017 		return static_cast<int>((act->hasConfig = 1) == 1);
1018 	}
1019 	else { return -1; }
1020 }
1021 
getActive(int o,std::string * val,const char ** desc,const char ** opt) const1022 int ClaspCliConfig::getActive(int o, std::string* val, const char** desc, const char** opt) const {
1023 	if      (isOption(o))     { return const_cast<ClaspCliConfig&>(*this).applyActive(o, 0, val, desc, opt); }
1024 	else if (!active())       { return -1; }
1025 	else if (o == meta_config){
1026 		const NodeKey& n = nodes_g[-o];
1027 		if (val)  {
1028 			uint8 k = (ConfigKey)active()->cliConfig;
1029 			if (k < config_max_value) { xconvert(*val, static_cast<ConfigKey>(k)); }
1030 			else                      { val->append(config_[!isGenerator()]); }
1031 		}
1032 		if (desc) { *desc = n.desc; }
1033 		if (opt)  { *opt  = n.name; }
1034 		return 1;
1035 	}
1036 	else { return -1; }
1037 }
setAppOpt(int o,const char * _val_)1038 int ClaspCliConfig::setAppOpt(int o, const char* _val_) {
1039 	if (o == meta_config) {
1040 		std::pair<ConfigKey, uint32> defC(config_default, INT_MAX);
1041 		if (Potassco::stringTo(_val_, defC)) { active()->cliConfig = (uint8)defC.first; }
1042 		else {
1043 			POTASSCO_EXPECT(std::ifstream(_val_).is_open(), "Could not open config file '%s'", _val_);
1044 			config_[!isGenerator()] = _val_; active()->cliConfig = config_max_value + !isGenerator();
1045 		}
1046 		return Range<uint32>(0, INT_MAX).clamp(defC.second);
1047 	}
1048 	else if (o == meta_tester && isGenerator()) {
1049 		addTesterConfig();
1050 		initTester_ = false;
1051 		RawConfig config("<tester>");
1052 		config.addArg(_val_);
1053 		ParsedOpts ex;
1054 		bool ret = ScopedSet(*this, mode_tester)->setConfig(config.iterator(), true, ParsedOpts(), &ex);
1055 		return ret && finalizeAppConfig(testerConfig(), finalizeParsed(testerConfig(), ex, ex), Problem_t::Asp, true);
1056 	}
1057 	return -1; // invalid option
1058 }
setAppDefaults(UserConfig * active,uint32 sId,const ParsedOpts & cmdLine,ProblemType t)1059 bool ClaspCliConfig::setAppDefaults(UserConfig* active, uint32 sId, const ParsedOpts& cmdLine, ProblemType t) {
1060 	ScopedSet temp(*this, (active == this ? 0 : mode_tester) | mode_relaxed, sId);
1061 	if (sId == 0 && t != Problem_t::Asp && cmdLine.count("sat-prepro") == 0) {
1062 		setActive(opt_sat_prepro, "2,iter=20,occ=25,time=120");
1063 	}
1064 	if (active->addSolver(sId).search == SolverParams::no_learning) {
1065 		if (cmdLine.count("heuristic") == 0) { setActive(opt_heuristic, "unit"); }
1066 		if (cmdLine.count("lookahead") == 0) { setActive(opt_lookahead, "atom"); }
1067 		if (cmdLine.count("deletion")  == 0) { setActive(opt_deletion, "no"); }
1068 		if (cmdLine.count("restarts")  == 0) { setActive(opt_restarts, "no"); }
1069 	}
1070 	return true;
1071 }
1072 
setConfig(const ConfigIter & config,bool allowMeta,const ParsedOpts & exclude,ParsedOpts * out)1073 bool ClaspCliConfig::setConfig(const ConfigIter& config, bool allowMeta, const ParsedOpts& exclude, ParsedOpts* out) {
1074 	createOptions();
1075 	ParseContext ctx(*this, config.name(), &exclude, allowMeta, out);
1076 	Potassco::ProgramOptions::parseCommandString(config.args(), ctx, Potassco::ProgramOptions::command_line_allow_flag_value);
1077 	return true;
1078 }
1079 
validate()1080 bool ClaspCliConfig::validate() {
1081 	UserConfiguration* arr[3] = { this, testerConfig(), 0 };
1082 	UserConfiguration** c     = arr;
1083 	const char* ctx = *c == this ? "config":"tester";
1084 	const char* err = 0;
1085 	do {
1086 		for (uint32 i = 0; i != (*c)->numSolver(); ++i) {
1087 			POTASSCO_REQUIRE((err = Clasp::Cli::validate((*c)->solver(i), (*c)->search(i))) == 0, "<%s>.%u: %s", ctx, i, err);
1088 		}
1089 	} while (*++c);
1090 	return true;
1091 }
1092 
finalize(const ParsedOpts & x,ProblemType t,bool defs)1093 bool ClaspCliConfig::finalize(const ParsedOpts& x, ProblemType t, bool defs) {
1094 	ParsedOpts temp;
1095 	return finalizeAppConfig(this, finalizeParsed(this, x, temp), t, defs);
1096 }
1097 
addDisabled(ParsedOpts & parsed)1098 void ClaspCliConfig::addDisabled(ParsedOpts& parsed) {
1099 	finalizeParsed(this, parsed, parsed);
1100 }
1101 
finalizeParsed(UserConfig * active,const ParsedOpts & parsed,ParsedOpts & exclude) const1102 const ClaspCliConfig::ParsedOpts& ClaspCliConfig::finalizeParsed(UserConfig* active, const ParsedOpts& parsed, ParsedOpts& exclude) const {
1103 	bool copied = &parsed == &exclude;
1104 	if (active->search(0).reduce.fReduce() == 0 && parsed.count("deletion") != 0) {
1105 		if (!copied) { exclude = parsed; copied = true; }
1106 		exclude.add("del-cfl");
1107 		exclude.add("del-max");
1108 		exclude.add("del-grow");
1109 	}
1110 	return !copied ? parsed : exclude;
1111 }
1112 
finalizeAppConfig(UserConfig * active,const ParsedOpts & parsed,ProblemType t,bool defs)1113 bool ClaspCliConfig::finalizeAppConfig(UserConfig* active, const ParsedOpts& parsed, ProblemType t, bool defs) {
1114 	if (defs && !setAppDefaults(active, 0, parsed, t)) { return false; }
1115 	SolverParams defSolver = active->solver(0);
1116 	SolveParams  defSearch = active->search(0);
1117 	if (active->hasConfig) { return true; }
1118 	uint8 c = active->cliConfig;
1119 	if (c == config_many && solve.numSolver() == 1) { c = config_default; }
1120 	if (c == config_default) {
1121 		if      (defSolver.search == SolverParams::no_learning)       { c = config_nolearn; }
1122 		else if (active == testerConfig())                            { c = config_tester_default; }
1123 		else if (solve.numSolver() == 1 || !solve.defaultPortfolio()) { c = t == Problem_t::Asp ? (uint8)config_asp_default : (uint8)config_sat_default; }
1124 		else                                                          { c = config_many; }
1125 	}
1126 	std::string m;
1127 	ConfigIter conf = getConfig(c, m);
1128 	uint8  mode     = (active == testerConfig() ? mode_tester : 0) | mode_relaxed;
1129 	uint32 portSize = 0;
1130 	const char* ctx = active == testerConfig() ? "tester" : "config", *err = 0;
1131 	for (uint32 i = 0; i != solve.numSolver() && conf.valid(); ++i) {
1132 		SolverParams& solver = (active->addSolver(i) = defSolver).setId(i);
1133 		SolveParams&  search = (active->addSearch(i) = defSearch);
1134 		ConfigKey     baseK  = config_default;
1135 		POTASSCO_REQUIRE(!*conf.base() || Potassco::stringTo(conf.base(), baseK), "<%s>.%s: '%s': Invalid base config!", ctx, conf.name(), conf.base());
1136 		if (baseK != config_default && !ScopedSet(*this, mode|mode_solver, i)->setConfig(getConfig(baseK), false, parsed, 0)) {
1137 			return false;
1138 		}
1139 		if (!ScopedSet(*this, mode, i)->setConfig(conf, false, parsed, 0)) {
1140 			return false;
1141 		}
1142 		POTASSCO_REQUIRE((err = Clasp::Cli::validate(solver, search)) == 0, "<%s>.%s : %s", ctx, conf.name(), err);
1143 		++portSize;
1144 		conf.next();
1145 		mode |= mode_solver;
1146 	}
1147 	active->hasConfig = 1;
1148 	return true;
1149 }
1150 
setAppConfig(const RawConfig & config,ProblemType t)1151 bool ClaspCliConfig::setAppConfig(const RawConfig& config, ProblemType t) {
1152 	Potassco::ProgramOptions::ParsedOptions exclude;
1153 	reset();
1154 	return setConfig(config.iterator(), true, exclude, &exclude) && assignDefaults(exclude) && finalize(exclude, t, true);
1155 }
1156 
validate(const SolverParams & solver,const SolveParams & search)1157 const char* validate(const SolverParams& solver, const SolveParams& search) {
1158 	const ReduceParams& reduce = search.reduce;
1159 	if (solver.search == SolverParams::no_learning) {
1160 		if (Heuristic_t::isLookback(solver.heuId)) return "Heuristic requires lookback strategy!";
1161 		if (!search.restart.sched.disabled() && !search.restart.sched.defaulted()) return "'no-lookback': restart options disabled!";
1162 		if (!reduce.cflSched.disabled() || (!reduce.growSched.disabled() && !reduce.growSched.defaulted()) || search.reduce.fReduce() != 0) return "'no-lookback': deletion options disabled!";
1163 	}
1164 	bool  hasSched = !reduce.cflSched.disabled() || !reduce.growSched.disabled() || reduce.maxRange != UINT32_MAX;
1165 	if (hasSched  && reduce.fReduce() == 0.0f && !reduce.growSched.defaulted()) return "'no-deletion': deletion strategies disabled!";
1166 	if (!hasSched && reduce.fReduce() != 0.0f && !reduce.growSched.defaulted()) return "'deletion': deletion strategy required!";
1167 	return 0;
1168 }
1169 }}
1170