Branch data Line data Source code
1 : : /******************************************************************************
2 : : * This file is part of the cvc5 project.
3 : : *
4 : : * Copyright (c) 2009-2026 by the authors listed in the file AUTHORS
5 : : * in the top-level source directory and their institutional affiliations.
6 : : * All rights reserved. See the file COPYING in the top-level source
7 : : * directory for licensing information.
8 : : * ****************************************************************************
9 : : *
10 : : * Implementation of the theory of strings.
11 : : */
12 : :
13 : : #include "theory/strings/theory_strings.h"
14 : :
15 : : #include "expr/attribute.h"
16 : : #include "expr/bound_var_manager.h"
17 : : #include "expr/kind.h"
18 : : #include "expr/sequence.h"
19 : : #include "options/smt_options.h"
20 : : #include "options/strings_options.h"
21 : : #include "options/theory_options.h"
22 : : #include "printer/smt2/smt2_printer.h"
23 : : #include "smt/logic_exception.h"
24 : : #include "theory/decision_manager.h"
25 : : #include "theory/ext_theory.h"
26 : : #include "theory/rewriter.h"
27 : : #include "theory/strings/theory_strings_utils.h"
28 : : #include "theory/strings/type_enumerator.h"
29 : : #include "theory/strings/word.h"
30 : : #include "theory/theory_model.h"
31 : : #include "theory/valuation.h"
32 : :
33 : : using namespace std;
34 : : using namespace cvc5::context;
35 : : using namespace cvc5::internal::kind;
36 : :
37 : : namespace cvc5::internal {
38 : : namespace theory {
39 : : namespace strings {
40 : :
41 : 51092 : TheoryStrings::TheoryStrings(Env& env, OutputChannel& out, Valuation valuation)
42 : : : Theory(THEORY_STRINGS, env, out, valuation),
43 : 51092 : d_notify(*this),
44 : 51092 : d_statistics(statisticsRegistry()),
45 : 51092 : d_state(env, d_valuation),
46 : 51092 : d_termReg(env, *this, d_state),
47 : 51092 : d_arithEntail(
48 : : env.getNodeManager(),
49 [ + + ]: 51092 : options().strings.stringRecArithApprox ? env.getRewriter() : nullptr,
50 : 51092 : options().strings.stringRecArithApprox),
51 : 51092 : d_strEntail(d_env.getRewriter(), d_arithEntail),
52 : 102184 : d_rewriter(env.getNodeManager(),
53 : 51092 : d_arithEntail,
54 : 51092 : d_strEntail,
55 : : &d_statistics.d_rewrites,
56 : : d_termReg.getAlphabetCardinality()),
57 [ + - ]: 102184 : d_eagerSolver(options().strings.stringEagerSolver
58 : 51092 : ? new EagerSolver(env, d_state)
59 : : : nullptr),
60 : 51092 : d_extTheoryCb(),
61 : 51092 : d_im(env, *this, d_state, d_termReg, d_extTheory, d_statistics),
62 : 51092 : d_extTheory(env, d_extTheoryCb, d_im),
63 : : // the checker depends on the cardinality of the alphabet
64 : 51092 : d_checker(nodeManager(), d_termReg.getAlphabetCardinality()),
65 : 51092 : d_bsolver(env, d_state, d_im, d_termReg),
66 : 51092 : d_csolver(env, d_state, d_im, d_termReg, d_bsolver),
67 : 51092 : d_esolver(env,
68 : 51092 : d_state,
69 : 51092 : d_im,
70 : 51092 : d_termReg,
71 : 51092 : d_rewriter,
72 : 51092 : d_bsolver,
73 : 51092 : d_csolver,
74 : 51092 : d_extTheory,
75 : 51092 : d_statistics),
76 : 51092 : d_psolver(env, d_state, d_im, d_termReg, d_bsolver, d_csolver),
77 : 51092 : d_asolver(env,
78 : 51092 : d_state,
79 : 51092 : d_im,
80 : 51092 : d_termReg,
81 : 51092 : d_bsolver,
82 : 51092 : d_csolver,
83 : 51092 : d_esolver,
84 : 51092 : d_extTheory),
85 : 51092 : d_rsolver(
86 : 51092 : env, d_state, d_im, d_termReg, d_csolver, d_esolver, d_statistics),
87 : 102184 : d_regexp_elim(
88 : : env,
89 : 51092 : options().strings.regExpElim == options::RegExpElimMode::AGG,
90 : 51092 : userContext()),
91 : 51092 : d_stringsFmf(env, valuation, d_termReg),
92 : 51092 : d_mcd(env, d_state, d_csolver),
93 : 51092 : d_strat(d_env),
94 : 51092 : d_absModelCounter(0),
95 : 51092 : d_strGapModelCounter(0),
96 : 51092 : d_cpacb(*this),
97 [ + + ][ + + ]: 61877 : d_psrewPg(env.isTheoryProofProducing()
[ - - ]
98 : : ? new TrustProofGenerator(
99 : 10785 : env, TrustId::STRINGS_PP_STATIC_REWRITE, {})
100 : 51092 : : nullptr)
101 : : {
102 : 51092 : d_termReg.finishInit(&d_im);
103 : :
104 : 51092 : d_zero = nodeManager()->mkConstInt(Rational(0));
105 : 51092 : d_one = nodeManager()->mkConstInt(Rational(1));
106 : 51092 : d_neg_one = nodeManager()->mkConstInt(Rational(-1));
107 : 51092 : d_true = nodeManager()->mkConst(true);
108 : 51092 : d_false = nodeManager()->mkConst(false);
109 : :
110 : : // set up the extended function callback
111 : 51092 : d_extTheoryCb.d_esolver = &d_esolver;
112 : :
113 : : // use the state object as the official theory state
114 : 51092 : d_theoryState = &d_state;
115 : : // use the inference manager as the official inference manager
116 : 51092 : d_inferManager = &d_im;
117 : 51092 : }
118 : :
119 : 101488 : TheoryStrings::~TheoryStrings() {}
120 : :
121 : 51092 : TheoryRewriter* TheoryStrings::getTheoryRewriter() { return &d_rewriter; }
122 : :
123 : 19899 : ProofRuleChecker* TheoryStrings::getProofChecker() { return &d_checker; }
124 : :
125 : 51092 : bool TheoryStrings::needsEqualityEngine(EeSetupInfo& esi)
126 : : {
127 : 51092 : esi.d_notify = &d_notify;
128 : 51092 : esi.d_name = "theory::strings::ee";
129 : 51092 : esi.d_notifyNewClass = true;
130 : 51092 : esi.d_notifyMerge = true;
131 : 51092 : esi.d_notifyDisequal = true;
132 : 51092 : return true;
133 : : }
134 : :
135 : 51092 : void TheoryStrings::finishInit()
136 : : {
137 [ - + ][ - + ]: 51092 : Assert(d_equalityEngine != nullptr);
[ - - ]
138 : :
139 : : // witness is used to eliminate str.from_code
140 : 51092 : d_valuation.setUnevaluatedKind(Kind::WITNESS);
141 : :
142 : 51092 : bool eagerEval = options().strings.stringEagerEval;
143 : : // The kinds we are treating as function application in congruence
144 : 51092 : d_equalityEngine->addFunctionKind(Kind::STRING_LENGTH, eagerEval);
145 : 51092 : d_equalityEngine->addFunctionKind(Kind::STRING_CONCAT, eagerEval);
146 : 51092 : d_equalityEngine->addFunctionKind(Kind::STRING_IN_REGEXP, eagerEval);
147 : 51092 : d_equalityEngine->addFunctionKind(Kind::STRING_TO_CODE, eagerEval);
148 : 51092 : d_equalityEngine->addFunctionKind(Kind::SEQ_UNIT, eagerEval);
149 : 51092 : d_equalityEngine->addFunctionKind(Kind::STRING_UNIT, false);
150 : : // `seq.nth` is not always defined, and so we do not evaluate it eagerly.
151 : 51092 : d_equalityEngine->addFunctionKind(Kind::SEQ_NTH, false);
152 : : // extended functions
153 : 51092 : d_equalityEngine->addFunctionKind(Kind::STRING_CONTAINS, eagerEval);
154 : 51092 : d_equalityEngine->addFunctionKind(Kind::STRING_LEQ, eagerEval);
155 : 51092 : d_equalityEngine->addFunctionKind(Kind::STRING_SUBSTR, eagerEval);
156 : 51092 : d_equalityEngine->addFunctionKind(Kind::STRING_UPDATE, eagerEval);
157 : 51092 : d_equalityEngine->addFunctionKind(Kind::STRING_ITOS, eagerEval);
158 : 51092 : d_equalityEngine->addFunctionKind(Kind::STRING_STOI, eagerEval);
159 : 51092 : d_equalityEngine->addFunctionKind(Kind::STRING_INDEXOF, eagerEval);
160 : 51092 : d_equalityEngine->addFunctionKind(Kind::STRING_INDEXOF_RE, eagerEval);
161 : 51092 : d_equalityEngine->addFunctionKind(Kind::STRING_REPLACE, eagerEval);
162 : 51092 : d_equalityEngine->addFunctionKind(Kind::STRING_REPLACE_ALL, eagerEval);
163 : 51092 : d_equalityEngine->addFunctionKind(Kind::STRING_REPLACE_RE, eagerEval);
164 : 51092 : d_equalityEngine->addFunctionKind(Kind::STRING_REPLACE_RE_ALL, eagerEval);
165 : 51092 : d_equalityEngine->addFunctionKind(Kind::STRING_REPLACE_ALL, eagerEval);
166 : 51092 : d_equalityEngine->addFunctionKind(Kind::STRING_TO_LOWER, eagerEval);
167 : 51092 : d_equalityEngine->addFunctionKind(Kind::STRING_TO_UPPER, eagerEval);
168 : 51092 : d_equalityEngine->addFunctionKind(Kind::STRING_REV, eagerEval);
169 : :
170 : : // memberships are not relevant for model building
171 : 51092 : d_valuation.setIrrelevantKind(Kind::STRING_IN_REGEXP);
172 : 51092 : d_valuation.setIrrelevantKind(Kind::STRING_LEQ);
173 : : // seq nth doesn't always evaluate
174 : 51092 : d_valuation.setSemiEvaluatedKind(Kind::SEQ_NTH);
175 : 51092 : }
176 : :
177 : 0 : std::string TheoryStrings::identify() const
178 : : {
179 : 0 : return std::string("TheoryStrings");
180 : : }
181 : :
182 : 2203527 : bool TheoryStrings::propagateLit(TNode literal)
183 : : {
184 [ + + ]: 2203527 : if (d_state.hasPendingConflict())
185 : : {
186 : : // pending conflict also implies we are done
187 : 1105 : return false;
188 : : }
189 : 2202422 : return d_im.propagateLit(literal);
190 : : }
191 : :
192 : 122372 : TrustNode TheoryStrings::explain(TNode literal)
193 : : {
194 [ + - ]: 122372 : Trace("strings-explain") << "explain called on " << literal << std::endl;
195 : 122372 : return d_im.explainLit(literal);
196 : : }
197 : :
198 : 50519 : void TheoryStrings::presolve()
199 : : {
200 [ + - ]: 101038 : Trace("strings-presolve")
201 : 0 : << "TheoryStrings::Presolving : get fmf options "
202 [ - - ]: 50519 : << (options().strings.stringFMF ? "true" : "false") << std::endl;
203 : 50519 : d_strat.initializeStrategy();
204 : :
205 : : // if strings fmf is enabled, register the strategy
206 [ + + ]: 50519 : if (options().strings.stringFMF)
207 : : {
208 : 85 : d_stringsFmf.presolve();
209 : : // This strategy is local to a check-sat call, since we refresh the strategy
210 : : // on every call to presolve.
211 : 85 : d_im.getDecisionManager()->registerStrategy(
212 : : DecisionManager::STRAT_STRINGS_SUM_LENGTHS,
213 : : d_stringsFmf.getDecisionStrategy(),
214 : : DecisionManager::STRAT_SCOPE_LOCAL_SOLVE);
215 : : }
216 [ + - ]: 50519 : Trace("strings-presolve") << "Finished presolve" << std::endl;
217 : 50519 : }
218 : :
219 : : /////////////////////////////////////////////////////////////////////////////
220 : : // MODEL GENERATION
221 : : /////////////////////////////////////////////////////////////////////////////
222 : :
223 : 15427 : bool TheoryStrings::collectModelValues(TheoryModel* m,
224 : : const std::set<Node>& termSet)
225 : : {
226 : 15427 : d_absModelCounter = 0;
227 : 15427 : d_strGapModelCounter = 0;
228 [ - + ]: 15427 : if (TraceIsOn("strings-debug-model"))
229 : : {
230 [ - - ]: 0 : Trace("strings-debug-model")
231 : 0 : << "TheoryStrings::collectModelValues" << std::endl;
232 [ - - ]: 0 : Trace("strings-debug-model") << "Equivalence classes are:" << std::endl;
233 : 0 : Trace("strings-debug-model") << debugPrintStringsEqc() << std::endl;
234 [ - - ]: 0 : Trace("strings-debug-model") << "Extended functions are:" << std::endl;
235 : 0 : Trace("strings-debug-model") << d_esolver.debugPrintModel() << std::endl;
236 : : }
237 [ + - ]: 15427 : Trace("strings-model") << "TheoryStrings::collectModelValues" << std::endl;
238 : : // Collects representatives by types and orders sequence types by how nested
239 : : // they are
240 : 15427 : std::map<TypeNode, std::unordered_set<Node>> repSet;
241 : 15427 : std::unordered_set<TypeNode> toProcess;
242 : : // Generate model
243 : 15427 : ModelCons* mc = d_state.getModelConstructor();
244 [ - + ][ - + ]: 15427 : Assert(mc != nullptr);
[ - - ]
245 : : // get the relevant string equivalence classes
246 : 15427 : std::vector<Node> auxEq;
247 : 15427 : mc->getStringRepresentativesFrom(termSet, toProcess, repSet, auxEq);
248 : : // assert the auxiliary equalities
249 [ - + ]: 15427 : for (const Node& aeq : auxEq)
250 : : {
251 : 0 : Assert(aeq.getKind() == Kind::EQUAL);
252 [ - - ]: 0 : Trace("strings-model") << "-> auxiliary equality " << aeq << std::endl;
253 [ - - ]: 0 : if (!m->assertEquality(aeq[0], aeq[1], true))
254 : : {
255 : 0 : Unreachable() << "TheoryStrings::collectModelValues: Inconsistent "
256 : 0 : "auxiliary equality"
257 : 0 : << std::endl;
258 : : return false;
259 : : }
260 : : }
261 : :
262 [ + + ]: 17634 : while (!toProcess.empty())
263 : : {
264 : : // Pick one of the remaining types to collect model values for
265 : 2213 : TypeNode tn = *toProcess.begin();
266 [ + + ]: 2213 : if (!collectModelInfoType(tn, toProcess, repSet, m))
267 : : {
268 : 6 : return false;
269 : : }
270 [ + + ]: 2213 : }
271 : :
272 : 15421 : return true;
273 : 15427 : }
274 : :
275 : : /**
276 : : * Object to sort by the value of pairs in the write model returned by the
277 : : * sequences array solver.
278 : : */
279 : : struct SortSeqIndex
280 : : {
281 : 32 : SortSeqIndex() {}
282 : : /** the comparison */
283 : 0 : bool operator()(const std::pair<Node, Node>& i,
284 : : const std::pair<Node, Node>& j)
285 : : {
286 : 0 : Assert(i.first.isConst() && i.first.getType().isInteger()
287 : : && j.first.isConst() && j.first.getType().isInteger());
288 : 0 : return i.first.getConst<Rational>() < j.first.getConst<Rational>();
289 : : }
290 : : };
291 : :
292 : 2218 : bool TheoryStrings::collectModelInfoType(
293 : : TypeNode tn,
294 : : std::unordered_set<TypeNode>& toProcess,
295 : : const std::map<TypeNode, std::unordered_set<Node>>& repSet,
296 : : TheoryModel* m)
297 : : {
298 : : // Make sure that the model values for the element type of sequences are
299 : : // computed first
300 [ + + ][ + + ]: 2218 : if (tn.isSequence() && tn.getSequenceElementType().isSequence())
[ + + ][ + + ]
[ - - ]
301 : : {
302 : 15 : TypeNode tnElem = tn.getSequenceElementType();
303 : 15 : if (toProcess.find(tnElem) != toProcess.end()
304 [ + + ][ + + ]: 30 : && !collectModelInfoType(tnElem, toProcess, repSet, m))
[ + + ][ + + ]
[ - - ]
305 : : {
306 : 2 : return false;
307 : : }
308 [ + + ]: 15 : }
309 : 2216 : toProcess.erase(tn);
310 : :
311 : 2216 : SEnumLenSet sels;
312 : 2216 : ModelCons* mc = d_state.getModelConstructor();
313 : : // get partition of strings of equal lengths for the representatives of the
314 : : // current type
315 : 2216 : std::vector<std::vector<Node>> col;
316 : 2216 : std::vector<Node> lts;
317 : 2216 : const std::vector<Node> repVec(repSet.at(tn).begin(), repSet.at(tn).end());
318 : 2216 : mc->separateByLength(m, repVec, col, lts);
319 [ - + ][ - + ]: 2216 : Assert(col.size() == lts.size());
[ - - ]
320 : : // indices in col that have lengths that are too big to represent
321 : 2216 : std::unordered_set<size_t> oobIndices;
322 : :
323 : 2216 : NodeManager* nm = nodeManager();
324 : 2216 : std::map<Node, Node> processed;
325 : : // step 1 : get all values for known lengths
326 : 2216 : std::vector<Node> lts_values;
327 : : // mapping from lengths used to the index in col that used that length
328 : 2216 : std::map<size_t, size_t> values_used;
329 : : // A list of pairs of indices in col that used the same length term. We use
330 : : // this as candidates to add length splitting on below (STRINGS_CMI_SPLIT),
331 : : // which is used as a safeguard when model construction fails unexpectedly
332 : : // by running out of values.
333 : 2216 : std::vector<std::pair<size_t, size_t>> len_splits;
334 [ + + ]: 12649 : for (size_t i = 0, csize = col.size(); i < csize; i++)
335 : : {
336 [ + - ]: 10433 : Trace("strings-model") << "Checking length for { " << col[i];
337 [ + - ]: 10433 : Trace("strings-model") << " } (length is " << lts[i] << ")" << std::endl;
338 : 10433 : Node len_value = lts[i];
339 [ - + ]: 10433 : if (len_value.isNull())
340 : : {
341 : 0 : lts_values.push_back(Node::null());
342 : : }
343 : 10433 : else if (len_value.getConst<Rational>()
344 [ + + ]: 20866 : > options().strings.stringsModelMaxLength)
345 : : {
346 : : // note that we give a warning instead of throwing logic exception if we
347 : : // cannot construct the string, these are then assigned witness terms
348 : : // below
349 : 68 : warning()
350 : 136 : << "The model was computed to have strings of length " << len_value
351 : : << ". Based on the current value of option --strings-model-max-len, "
352 : 68 : "we only allow strings up to length "
353 : 68 : << options().strings.stringsModelMaxLength << std::endl;
354 : 68 : oobIndices.insert(i);
355 : 68 : lts_values.push_back(len_value);
356 : : }
357 : : else
358 : : {
359 : : std::size_t lvalue =
360 : 10365 : len_value.getConst<Rational>().getNumerator().toUnsignedInt();
361 : 10365 : auto itvu = values_used.find(lvalue);
362 [ + + ]: 10365 : if (itvu == values_used.end())
363 : : {
364 : 8434 : values_used[lvalue] = i;
365 : : }
366 : : else
367 : : {
368 : 1931 : len_splits.emplace_back(i, itvu->second);
369 : : }
370 : 10365 : lts_values.push_back(len_value);
371 : : }
372 : 10433 : }
373 : : ////step 2 : assign arbitrary values for unknown lengths?
374 : : // confirmed by calculus invariant, see paper
375 [ + - ]: 2216 : Trace("strings-model") << "Assign to equivalence classes..." << std::endl;
376 : 2216 : std::map<Node, Node> pure_eq_assign;
377 : : // if we are using the sequences array solver, get the connected sequences
378 : 2216 : const std::map<Node, Node>* conSeq = nullptr;
379 : 2216 : std::map<Node, Node>::const_iterator itcs;
380 [ + + ]: 2216 : if (options().strings.seqArray != options::SeqArrayMode::NONE)
381 : : {
382 : 62 : conSeq = &d_asolver.getConnectedSequences();
383 : : }
384 : : // step 3 : assign values to equivalence classes that are pure variables
385 [ + + ]: 12629 : for (size_t i = 0, csize = col.size(); i < csize; i++)
386 : : {
387 : 10419 : bool wasOob = (oobIndices.find(i) != oobIndices.end());
388 : 10419 : std::vector<Node> pure_eq;
389 : 10419 : Node lenValue = lts_values[i];
390 [ + - ]: 20838 : Trace("strings-model") << "Considering " << col[i].size()
391 : 0 : << " equivalence classes of type " << tn
392 : 10419 : << " for length " << lenValue << std::endl;
393 [ + + ]: 25421 : for (const Node& eqc : col[i])
394 : : {
395 [ + - ]: 15002 : Trace("strings-model") << "- eqc: " << eqc << std::endl;
396 : : // check if col[i][j] has only variables
397 [ + + ]: 15002 : if (eqc.isConst())
398 : : {
399 : 9004 : processed[eqc] = eqc;
400 : : // Make sure that constants are asserted to the theory model that we
401 : : // are building. It is possible that new constants were introduced by
402 : : // the eager evaluation in the equality engine. These terms are missing
403 : : // in the term set and, as a result, are skipped when the equality
404 : : // engine is asserted to the theory model.
405 : 9004 : m->getEqualityEngine()->addTerm(eqc);
406 : :
407 : : // For sequences constants, also add the elements (expanding elements
408 : : // as necessary)
409 [ + + ]: 9004 : if (eqc.getType().isSequence())
410 : : {
411 : 365 : const std::vector<Node> elems = eqc.getConst<Sequence>().getVec();
412 : 365 : std::vector<TNode> visit(elems.begin(), elems.end());
413 [ + + ]: 574 : for (size_t j = 0; j < visit.size(); j++)
414 : : {
415 : 209 : Node se = visit[j];
416 [ - + ][ - + ]: 209 : Assert(se.isConst());
[ - - ]
417 [ + + ]: 209 : if (se.getType().isSequence())
418 : : {
419 : 16 : const std::vector<Node> selems = se.getConst<Sequence>().getVec();
420 : 16 : visit.insert(visit.end(), selems.begin(), selems.end());
421 : 16 : }
422 : 209 : m->getEqualityEngine()->addTerm(se);
423 : 209 : }
424 : 365 : }
425 : :
426 [ + - ]: 9004 : Trace("strings-model") << "-> constant" << std::endl;
427 : 12372 : continue;
428 : 9004 : }
429 : 5998 : std::vector<Node> nfe = mc->getNormalForm(eqc);
430 [ + + ]: 5998 : if (nfe.size() != 1)
431 : : {
432 : : // will be assigned via a concatenation of normal form eqc
433 [ + - ]: 6634 : Trace("strings-model")
434 : 3317 : << " -> will be assigned by normal form " << nfe << std::endl;
435 : 3317 : continue;
436 : : }
437 : : // check if the length is too big to represent
438 [ + + ]: 2681 : if (wasOob)
439 : : {
440 : 51 : processed[eqc] = eqc;
441 [ + - ][ + - ]: 51 : Assert(!lenValue.isNull() && lenValue.isConst());
[ - + ][ - + ]
[ - - ]
442 : : // make the abstract value (witness ((x String)) (= (str.len x)
443 : : // lenValue))
444 : : Node w = utils::mkAbstractStringValueForLength(
445 : 102 : eqc, lenValue, d_absModelCounter);
446 : 51 : d_absModelCounter++;
447 [ + - ]: 102 : Trace("strings-model")
448 : 51 : << "-> length out of bounds, assign abstract " << w << std::endl;
449 [ - + ]: 51 : if (!m->assertEquality(eqc, w, true))
450 : : {
451 : 0 : Unreachable() << "TheoryStrings::collectModelInfoType: Inconsistent "
452 : 0 : "abstract equality"
453 : 0 : << std::endl;
454 : : return false;
455 : : }
456 : 51 : continue;
457 [ - + ]: 51 : }
458 : : // ensure we have decided on length value at this point
459 [ - + ]: 2630 : if (lenValue.isNull())
460 : : {
461 : : // start with length two (other lengths have special precendence)
462 : 0 : size_t lvalue = 2;
463 [ - - ]: 0 : while (values_used.find(lvalue) != values_used.end())
464 : : {
465 : 0 : lvalue++;
466 : : }
467 [ - - ]: 0 : Trace("strings-model")
468 : 0 : << "*** Decide to make length of " << lvalue << std::endl;
469 : 0 : lenValue = nm->mkConstInt(Rational(lvalue));
470 : 0 : values_used[lvalue] = i;
471 : : }
472 : : // is it an equivalence class with a seq.unit term?
473 : 2630 : Node assignedValue;
474 [ - + ]: 2630 : if (nfe[0].getKind() == Kind::STRING_UNIT)
475 : : {
476 : : // str.unit is applied to integers, where we are guaranteed the model
477 : : // exists. We preempitively get the model value here, so that we
478 : : // avoid repeated model values for strings.
479 : 0 : Node val = d_valuation.getCandidateModelValue(nfe[0][0]);
480 : 0 : assignedValue = utils::mkUnit(eqc.getType(), val);
481 : 0 : assignedValue = rewrite(assignedValue);
482 [ - - ]: 0 : Trace("strings-model")
483 : 0 : << "-> assign via str.unit: " << assignedValue << std::endl;
484 : 0 : }
485 [ + + ]: 2630 : else if (nfe[0].getKind() == Kind::SEQ_UNIT)
486 : : {
487 [ + + ]: 156 : if (nfe[0][0].getType().isStringLike())
488 : : {
489 : : // By this point, we should have assigned model values for the
490 : : // elements of this sequence type because of the check in the
491 : : // beginning of this method
492 : 16 : Node argVal = m->getRepresentative(nfe[0][0]);
493 [ - + ][ - + ]: 8 : Assert(nfe[0].getKind() == Kind::SEQ_UNIT);
[ - - ]
494 : 8 : assignedValue = utils::mkUnit(eqc.getType(), argVal);
495 : 8 : }
496 : : else
497 : : {
498 : : // Otherwise, we use the term itself. We cannot get the model
499 : : // value of this term, since it might not be available yet, as
500 : : // it may belong to a theory that has not built its model yet.
501 : : // Hence, we assign a (non-constant) skeleton (seq.unit argVal).
502 : 148 : assignedValue = nfe[0];
503 : : }
504 : 156 : assignedValue = rewrite(assignedValue);
505 [ + - ]: 312 : Trace("strings-model")
506 : 156 : << "-> assign via seq.unit: " << assignedValue << std::endl;
507 : : }
508 [ + + ][ + + ]: 2474 : else if (d_termReg.hasStringCode() && lenValue == d_one)
[ + + ]
509 : : {
510 : : // It has a code and the length of these equivalence classes are one.
511 : : // Note this code is solely for strings, not sequences.
512 : 1491 : EqcInfo* eip = d_state.getOrMakeEqcInfo(eqc, false);
513 [ + - ][ + + ]: 1491 : if (eip && !eip->d_codeTerm.get().isNull())
[ + + ]
514 : : {
515 : : // its value must be equal to its code
516 : 1312 : Node ct = nm->mkNode(Kind::STRING_TO_CODE, eip->d_codeTerm.get());
517 : 1312 : Node ctv = d_valuation.getCandidateModelValue(ct);
518 : : unsigned cvalue =
519 : 1312 : ctv.getConst<Rational>().getNumerator().toUnsignedInt();
520 [ + - ]: 1312 : Trace("strings-model") << "(code: " << cvalue << ") ";
521 : 1312 : std::vector<unsigned> vec;
522 : 1312 : vec.push_back(cvalue);
523 : 1312 : assignedValue = nm->mkConst(String(vec));
524 [ + - ]: 2624 : Trace("strings-model")
525 : 1312 : << "-> assign via str.code: " << assignedValue << std::endl;
526 : 1312 : }
527 : : }
528 [ + + ]: 983 : else if (options().strings.seqArray != options::SeqArrayMode::NONE)
529 : : {
530 : 100 : TypeNode eqcType = eqc.getType();
531 : : // determine skeleton based on the write model, if it exists
532 : 100 : const std::map<Node, Node>& writeModel = d_asolver.getWriteModel(eqc);
533 [ + + ]: 100 : if (!writeModel.empty())
534 : : {
535 [ + - ]: 64 : Trace("strings-model") << "Write model for " << eqc << " (type " << tn
536 : 32 : << ") is:" << std::endl;
537 : 32 : std::vector<std::pair<Node, Node>> writes;
538 : 32 : std::unordered_set<Node> usedWrites;
539 [ + + ]: 80 : for (const std::pair<const Node, Node>& w : writeModel)
540 : : {
541 [ + - ]: 48 : Trace("strings-model") << " " << w.first << " -> " << w.second;
542 : 48 : Node ivalue = d_valuation.getCandidateModelValue(w.first);
543 : 48 : Assert(ivalue.isConst() && ivalue.getType().isInteger());
544 : : // ignore if out of bounds
545 : 48 : Rational irat = ivalue.getConst<Rational>();
546 [ + + ][ + + ]: 48 : if (irat.sgn() == -1 || irat >= lenValue.getConst<Rational>())
[ + + ]
547 : : {
548 [ + - ]: 36 : Trace("strings-model")
549 : 18 : << " (index " << irat << " out of bounds)" << std::endl;
550 : 18 : continue;
551 : : }
552 [ + + ]: 30 : if (usedWrites.find(ivalue) != usedWrites.end())
553 : : {
554 [ + - ]: 4 : Trace("strings-model")
555 : 2 : << " (index " << irat << " already written)" << std::endl;
556 : 2 : continue;
557 : : }
558 [ + - ]: 28 : Trace("strings-model") << " (index " << irat << ")" << std::endl;
559 : 28 : usedWrites.insert(ivalue);
560 : 56 : Node wsunit = utils::mkUnit(eqcType, w.second);
561 : 28 : writes.emplace_back(ivalue, wsunit);
562 [ + + ][ + + ]: 68 : }
563 : : // sort based on index value
564 : 32 : SortSeqIndex ssi;
565 : 32 : std::sort(writes.begin(), writes.end(), ssi);
566 : 32 : std::vector<Node> cc;
567 : 32 : uint32_t currIndex = 0;
568 [ + + ]: 92 : for (size_t w = 0, wsize = writes.size(); w <= wsize; w++)
569 : : {
570 : : uint32_t nextIndex;
571 [ + + ]: 60 : if (w == writes.size())
572 : : {
573 : 32 : nextIndex =
574 : 32 : lenValue.getConst<Rational>().getNumerator().toUnsignedInt();
575 : : }
576 : : else
577 : : {
578 : 28 : Node windex = writes[w].first;
579 [ - + ][ - + ]: 28 : Assert(windex.getConst<Rational>()
[ - - ]
580 : : <= Rational(String::maxSize()));
581 : 28 : nextIndex =
582 : 28 : windex.getConst<Rational>().getNumerator().toUnsignedInt();
583 [ - + ][ - + ]: 28 : Assert(nextIndex >= currIndex);
[ - - ]
584 : 28 : }
585 [ + + ]: 60 : if (nextIndex > currIndex)
586 : : {
587 [ + - ]: 24 : Trace("strings-model") << "Make skeleton from " << currIndex
588 : 12 : << " ... " << nextIndex << std::endl;
589 : : // allocate arbitrary value to fill gap
590 [ - + ][ - + ]: 12 : Assert(conSeq != nullptr);
[ - - ]
591 : 12 : Node base = eqc;
592 : 12 : itcs = conSeq->find(eqc);
593 [ + + ]: 12 : if (itcs != conSeq->end())
594 : : {
595 : 4 : base = itcs->second;
596 : : }
597 : : // use a skeleton for the gap and not a concrete value, as we
598 : : // do not know how which values from the element type are
599 : : // allowable (i.e. unconstrained) to assign to the gap
600 : 12 : Node cgap = mkSkeletonFromBase(base, currIndex, nextIndex);
601 : 12 : cc.push_back(cgap);
602 : 12 : }
603 : : // then take read
604 [ + + ]: 60 : if (w < wsize)
605 : : {
606 : 28 : cc.push_back(writes[w].second);
607 : : }
608 : 60 : currIndex = nextIndex + 1;
609 : : }
610 : 32 : assignedValue = utils::mkConcat(cc, tn);
611 [ + - ]: 64 : Trace("strings-model")
612 : 0 : << "-> assign via seq.update/nth eqc: " << assignedValue
613 : 32 : << std::endl;
614 : 32 : }
615 : 100 : }
616 [ + + ]: 2630 : if (!assignedValue.isNull())
617 : : {
618 : 1500 : pure_eq_assign[eqc] = assignedValue;
619 : 1500 : m->getEqualityEngine()->addTerm(assignedValue);
620 : : }
621 : : else
622 : : {
623 [ + - ]: 1130 : Trace("strings-model") << "-> no assignment" << std::endl;
624 : : }
625 : 2630 : pure_eq.push_back(eqc);
626 [ + + ][ - ]: 5998 : }
627 : :
628 : : // assign a new length if necessary
629 [ + + ]: 10419 : if (!pure_eq.empty())
630 : : {
631 [ + - ]: 3056 : Trace("strings-model") << "Need to assign values of length " << lenValue
632 : 1528 : << " to equivalence classes ";
633 [ + + ]: 4158 : for (unsigned j = 0; j < pure_eq.size(); j++)
634 : : {
635 [ + - ]: 2630 : Trace("strings-model") << pure_eq[j] << " ";
636 : : }
637 [ + - ]: 1528 : Trace("strings-model") << std::endl;
638 : :
639 : : // use type enumerator
640 [ - + ][ - + ]: 1528 : Assert(lenValue.getConst<Rational>() <= Rational(String::maxSize()))
[ - - ]
641 : 0 : << "Exceeded UINT32_MAX in string model";
642 : : uint32_t currLen =
643 : 1528 : lenValue.getConst<Rational>().getNumerator().toUnsignedInt();
644 [ + - ]: 3056 : Trace("strings-model") << "Cardinality of alphabet is "
645 : 1528 : << d_termReg.getAlphabetCardinality() << std::endl;
646 : 1528 : SEnumLen* sel = sels.getEnumerator(currLen, tn);
647 [ + + ]: 4152 : for (const Node& eqc : pure_eq)
648 : : {
649 : 2630 : Node c;
650 : 2630 : std::map<Node, Node>::iterator itp = pure_eq_assign.find(eqc);
651 [ + + ]: 2630 : if (itp == pure_eq_assign.end())
652 : : {
653 : : do
654 : : {
655 [ + + ]: 1475 : if (sel->isFinished())
656 : : {
657 : : // We are in a case where model construction is impossible due to
658 : : // an insufficient number of constants of a given length.
659 : :
660 : : // Consider an integer equivalence class E whose value is assigned
661 : : // n in the model. Let { S_1, ..., S_m } be the set of string
662 : : // equivalence classes such that len( x ) is a member of E for
663 : : // some member x of each class S1, ...,Sm. Since our calculus is
664 : : // saturated with respect to cardinality inference (see Liang
665 : : // et al, Figure 6, CAV 2014), we have that m <= A^n, where A is
666 : : // the cardinality of our alphabet.
667 : :
668 : : // Now, consider the case where there exists two integer
669 : : // equivalence classes E1 and E2 that are assigned n, and moreover
670 : : // we did not received notification from arithmetic that E1 = E2.
671 : : // This typically should never happen, but assume in the following
672 : : // that it does.
673 : :
674 : : // Now, it may be the case that there are string equivalence
675 : : // classes { S_1, ..., S_m1 } whose lengths are in E1,
676 : : // and classes { S'_1, ..., S'_m2 } whose lengths are in E2, where
677 : : // m1 + m2 > A^n. In this case, we have insufficient strings to
678 : : // assign to { S_1, ..., S_m1, S'_1, ..., S'_m2 }. If this
679 : : // happens, we add a split on len( u1 ) = len( u2 ) for some
680 : : // len( u1 ) in E1, len( u2 ) in E2. We do this for each pair of
681 : : // integer equivalence classes that are assigned to the same value
682 : : // in the model.
683 [ - + ][ - + ]: 6 : AlwaysAssert(!len_splits.empty());
[ - - ]
684 [ + + ]: 16 : for (const std::pair<size_t, size_t>& sl : len_splits)
685 : : {
686 : : // ensure we use proxy variables or else the split may be
687 : : // rewritten away
688 : 10 : Node k1 = col[sl.first][0];
689 : 10 : Node kp1 = d_termReg.getProxyVariableFor(k1);
690 : 10 : Node k2 = col[sl.second][0];
691 : 10 : Node kp2 = d_termReg.getProxyVariableFor(k2);
692 : : Node s1 =
693 [ + + ]: 10 : nm->mkNode(Kind::STRING_LENGTH, kp1.isNull() ? k1 : kp1);
694 : : Node s2 =
695 [ + + ]: 10 : nm->mkNode(Kind::STRING_LENGTH, kp2.isNull() ? k2 : kp2);
696 : 10 : Node eq = s1.eqNode(s2);
697 : 20 : Node spl = nm->mkNode(Kind::OR, eq, eq.negate());
698 : 10 : d_im.lemma(spl, InferenceId::STRINGS_CMI_SPLIT);
699 [ + - ]: 20 : Trace("strings-lemma")
700 : 10 : << "Strings::CollectModelInfoSplit: " << spl << std::endl;
701 : 10 : }
702 : : // we added a lemma, so can return here
703 : 6 : return false;
704 : : }
705 : 1469 : c = sel->getCurrent();
706 : : // if we are a sequence with infinite element type
707 : 4407 : if (tn.isSequence()
708 [ + + ][ + + ]: 1469 : && !d_env.isFiniteType(tn.getSequenceElementType()))
[ + + ][ + + ]
[ - - ]
709 : : {
710 : : // Make a skeleton instead.
711 : 23 : c = mkSkeletonFor(c);
712 : : }
713 : : // increment
714 : 1469 : sel->increment();
715 [ + + ]: 1469 : } while (m->hasTerm(c));
716 : : }
717 : : else
718 : : {
719 : 1500 : c = itp->second;
720 : : }
721 [ + - ]: 5248 : Trace("strings-model")
722 : 2624 : << "*** Assigned constant " << c << " for " << eqc << std::endl;
723 : 2624 : processed[eqc] = c;
724 [ - + ]: 2624 : if (!m->assertEquality(eqc, c, true))
725 : : {
726 : : // this should never happen due to the model soundness argument
727 : : // for strings
728 : 0 : Unreachable()
729 : 0 : << "TheoryStrings::collectModelInfoType: Inconsistent equality"
730 : 0 : << std::endl;
731 : : return false;
732 : : }
733 [ + + ]: 2630 : }
734 : : }
735 [ + + ][ + + ]: 10425 : }
736 [ + - ]: 2210 : Trace("strings-model") << "String Model : Pure Assigned." << std::endl;
737 : : // step 4 : assign constants to all other equivalence classes
738 [ + + ]: 17194 : for (const Node& rn : repVec)
739 : : {
740 [ + + ]: 14984 : if (processed.find(rn) != processed.end())
741 : : {
742 : 11669 : continue;
743 : : }
744 : :
745 : 3315 : std::vector<Node> nf = mc->getNormalForm(rn);
746 [ - + ]: 3315 : if (TraceIsOn("strings-model"))
747 : : {
748 [ - - ]: 0 : Trace("strings-model")
749 : 0 : << "Construct model for " << rn << " based on normal form ";
750 [ - - ]: 0 : for (unsigned j = 0, size = nf.size(); j < size; j++)
751 : : {
752 : 0 : Node n = nf[j];
753 [ - - ]: 0 : if (j > 0)
754 : : {
755 [ - - ]: 0 : Trace("strings-model") << " ++ ";
756 : : }
757 [ - - ]: 0 : Trace("strings-model") << n;
758 : 0 : Node r = d_state.getRepresentative(n);
759 : 0 : if (!r.isConst() && processed.find(r) == processed.end())
760 : : {
761 [ - - ]: 0 : Trace("strings-model") << "(UNPROCESSED)";
762 : : }
763 : 0 : }
764 : : }
765 [ + - ]: 3315 : Trace("strings-model") << std::endl;
766 : 3315 : std::vector<Node> nc;
767 [ + + ]: 15342 : for (const Node& n : nf)
768 : : {
769 : 24054 : Node r = d_state.getRepresentative(n);
770 [ + + ][ + - ]: 12027 : Assert(r.isConst() || processed.find(r) != processed.end());
[ - + ][ - + ]
[ - - ]
771 [ + + ]: 12027 : nc.push_back(r.isConst() ? r : processed[r]);
772 : 12027 : }
773 : 3315 : Node cc = d_termReg.mkNConcat(nc, tn);
774 [ + - ]: 6630 : Trace("strings-model") << "*** Determined constant " << cc << " for " << rn
775 : 3315 : << std::endl;
776 : 3315 : processed[rn] = cc;
777 [ - + ]: 3315 : if (!m->assertEquality(rn, cc, true))
778 : : {
779 : : // this should never happen due to the model soundness argument
780 : : // for strings
781 : 0 : Unreachable() << "TheoryStrings::collectModelInfoType: "
782 : 0 : "Inconsistent equality (unprocessed eqc)"
783 : 0 : << std::endl;
784 : : return false;
785 : : }
786 [ + + ]: 3315 : else if (!cc.isConst())
787 : : {
788 : : // the value may be specified by seq.unit components, ensure this
789 : : // is marked as the skeleton for constructing values in this class.
790 : 67 : m->assertSkeleton(cc);
791 : : }
792 [ + - ][ + - ]: 3315 : }
[ + - ]
793 : : // Trace("strings-model") << "String Model : Assigned." << std::endl;
794 [ + - ]: 2210 : Trace("strings-model") << "String Model : Finished." << std::endl;
795 : 2210 : return true;
796 : 2216 : }
797 : :
798 : 23 : Node TheoryStrings::mkSkeletonFor(Node c)
799 : : {
800 : 23 : NodeManager* nm = nodeManager();
801 : 23 : SkolemManager* sm = nm->getSkolemManager();
802 : 23 : BoundVarManager* bvm = nm->getBoundVarManager();
803 : 23 : TypeNode tn = c.getType();
804 [ - + ][ - + ]: 23 : Assert(tn.isSequence());
[ - - ]
805 [ - + ][ - + ]: 23 : Assert(c.getKind() == Kind::CONST_SEQUENCE);
[ - - ]
806 : 23 : const Sequence& sn = c.getConst<Sequence>();
807 : 23 : const std::vector<Node>& snvec = sn.getVec();
808 : 23 : std::vector<Node> skChildren;
809 : 23 : TypeNode etn = tn.getSequenceElementType();
810 [ + + ]: 63 : for (const Node& snv : snvec)
811 : : {
812 [ - + ][ - + ]: 40 : Assert(snv.getType() == etn);
[ - - ]
813 : 80 : Node v = bvm->mkBoundVar(BoundVarId::STRINGS_SEQ_MODEL, snv, etn);
814 : : // use a skolem, not a bound variable
815 : 40 : Node kv = sm->mkPurifySkolem(v);
816 : 40 : skChildren.push_back(utils::mkUnit(tn, kv));
817 : 40 : }
818 : 69 : return utils::mkConcat(skChildren, c.getType());
819 : 23 : }
820 : :
821 : 12 : Node TheoryStrings::mkSkeletonFromBase(Node r,
822 : : size_t currIndex,
823 : : size_t nextIndex)
824 : : {
825 [ - + ][ - + ]: 12 : Assert(nextIndex > currIndex);
[ - - ]
826 [ - + ][ - + ]: 12 : Assert(!r.isNull());
[ - - ]
827 : 12 : NodeManager* nm = nodeManager();
828 : 12 : SkolemManager* sm = nm->getSkolemManager();
829 : 12 : TypeNode tn = r.getType();
830 : 12 : std::vector<Node> skChildren;
831 [ + - ]: 12 : if (tn.isSequence())
832 : : {
833 : 12 : std::vector<Node> cacheVals(2);
834 : 12 : cacheVals[0] = r;
835 : 12 : TypeNode etn = tn.getSequenceElementType();
836 [ + + ]: 24 : for (size_t i = currIndex; i < nextIndex; i++)
837 : : {
838 : 12 : cacheVals[1] = nm->mkConstInt(Rational(i));
839 : : Node kv = sm->mkInternalSkolemFunction(
840 : 12 : InternalSkolemId::SEQ_MODEL_BASE_ELEMENT, etn, cacheVals);
841 : 12 : skChildren.push_back(utils::mkUnit(tn, kv));
842 : 12 : }
843 : 12 : }
844 : : else
845 : : {
846 : : // allocate a unique symbolic (unspecified) string of length one, and
847 : : // repeat it (nextIndex-currIndex) times.
848 : : // Notice that this is guaranteed to be a unique (unspecified) character,
849 : : // since the only existing str.unit terms originate from our reductions,
850 : : // and hence are only applied to non-negative arguments. If the user
851 : : // was able to give arbitrary constraints over str.unit terms, then this
852 : : // construction would require a character not used in the model value of
853 : : // any other string.
854 : 0 : d_strGapModelCounter++;
855 : : Node symChar =
856 : 0 : utils::mkUnit(tn, nm->mkConstInt(-Rational(d_strGapModelCounter)));
857 : 0 : skChildren.resize(nextIndex - currIndex, symChar);
858 : 0 : }
859 : 24 : return utils::mkConcat(skChildren, tn);
860 : 12 : }
861 : :
862 : : /////////////////////////////////////////////////////////////////////////////
863 : : // MAIN SOLVER
864 : : /////////////////////////////////////////////////////////////////////////////
865 : :
866 : 364181 : void TheoryStrings::preRegisterTerm(TNode n)
867 : : {
868 [ + - ]: 728362 : Trace("strings-preregister")
869 : 364181 : << "TheoryStrings::preRegisterTerm: " << n << std::endl;
870 : 364181 : d_termReg.preRegisterTerm(n);
871 : : // Register the term with the extended theory. Notice we do not recurse on
872 : : // this term here since preRegisterTerm is already called recursively on all
873 : : // subterms in preregistered literals.
874 : 364181 : d_extTheory.registerTerm(n);
875 : 364181 : }
876 : :
877 : 2177833 : bool TheoryStrings::preNotifyFact(TNode atom,
878 : : bool pol,
879 : : CVC5_UNUSED TNode fact,
880 : : CVC5_UNUSED bool isPrereg,
881 : : bool isInternal)
882 : : {
883 [ + + ]: 2177833 : if (atom.getKind() == Kind::EQUAL)
884 : : {
885 : : // this is only required for internal facts, others are already registered
886 [ + + ]: 2124977 : if (isInternal)
887 : : {
888 : : // We must ensure these terms are registered. We register eagerly here for
889 : : // performance reasons. Alternatively, terms could be registered at full
890 : : // effort in e.g. BaseSolver::init.
891 [ + + ]: 295719 : for (const Node& t : atom)
892 : : {
893 : 197146 : d_termReg.registerTerm(t);
894 : 197146 : }
895 : : }
896 : : // store disequalities between strings that occur as literals
897 : 2124977 : if (!pol && atom[0].getType().isStringLike())
898 : : {
899 : 372896 : d_state.addDisequality(atom[0], atom[1]);
900 : : }
901 : : }
902 : 2177833 : return false;
903 : : }
904 : :
905 : 2177833 : void TheoryStrings::notifyFact(TNode atom,
906 : : bool polarity,
907 : : TNode fact,
908 : : bool isInternal)
909 : : {
910 [ + - ]: 2177833 : if (d_eagerSolver)
911 : : {
912 : 2177833 : d_eagerSolver->notifyFact(atom, polarity, fact, isInternal);
913 : : }
914 : : // process pending conflicts due to reasoning about endpoints
915 [ + + ][ + + ]: 2177833 : if (!d_state.isInConflict() && d_state.hasPendingConflict())
[ + + ]
916 : : {
917 : 1123 : InferInfo iiPendingConf(InferenceId::UNKNOWN);
918 : 1123 : d_state.getPendingConflict(iiPendingConf);
919 [ + - ]: 2246 : Trace("strings-pending")
920 : 1123 : << "Process pending conflict " << iiPendingConf.d_premises << std::endl;
921 [ + - ]: 2246 : Trace("strings-conflict")
922 : 1123 : << "CONFLICT: Eager : " << iiPendingConf.d_premises << std::endl;
923 : 1123 : ++(d_statistics.d_conflictsEager);
924 : : // call the inference manager to send the conflict
925 : 1123 : d_im.processConflict(iiPendingConf);
926 : 1123 : return;
927 : 1123 : }
928 : : // if not doing eager registration, we now register all subterms of the atom
929 [ + + ]: 2176710 : if (!options().strings.stringEagerReg)
930 : : {
931 : 1546 : d_termReg.registerSubterms(atom);
932 : : }
933 [ + - ]: 2176710 : Trace("strings-pending-debug") << " Now collect terms" << std::endl;
934 [ + - ]: 2176710 : Trace("strings-pending-debug") << " Finished collect terms" << std::endl;
935 : : }
936 : :
937 : 914531 : void TheoryStrings::postCheck(Effort e)
938 : : {
939 : 914531 : d_im.doPendingFacts();
940 : :
941 [ - + ][ - + ]: 914531 : Assert(d_strat.isStrategyInit());
[ - - ]
942 [ + + ]: 1822336 : if (!d_state.isInConflict() && !d_valuation.needCheck()
943 [ + + ][ + + ]: 1822336 : && d_strat.hasStrategyEffort(e))
[ + + ]
944 : : {
945 [ + - ]: 139318 : Trace("strings-check-debug")
946 : 69659 : << "Theory of strings " << e << " effort check " << std::endl;
947 [ - + ]: 69659 : if (TraceIsOn("strings-eqc"))
948 : : {
949 : 0 : Trace("strings-eqc") << debugPrintStringsEqc() << std::endl;
950 : : }
951 : : // Start the full effort check. This will compute the relevant term set,
952 : : // which is independent of the loop below, which adds internal facts.
953 : 69659 : d_termReg.notifyStartFullEffortCheck();
954 : 69659 : ++(d_statistics.d_checkRuns);
955 : 69659 : bool sentLemma = false;
956 : 69659 : bool hadPending = false;
957 [ + - ]: 69659 : Trace("strings-check") << "Check at effort " << e << "..." << std::endl;
958 : : do
959 : : {
960 : 105078 : d_im.reset();
961 : : // assume the default model constructor in case we answer sat after this
962 : : // check
963 : 105078 : d_state.setModelConstructor(&d_mcd);
964 : 105078 : ++(d_statistics.d_strategyRuns);
965 [ + - ]: 105078 : Trace("strings-check") << " * Run strategy..." << std::endl;
966 : 105078 : runStrategy(e);
967 : : // remember if we had pending facts or lemmas
968 : 105078 : hadPending = d_im.hasPending();
969 : : // Send the facts *and* the lemmas. We send lemmas regardless of whether
970 : : // we send facts since some lemmas cannot be dropped. Other lemmas are
971 : : // otherwise avoided by aborting the strategy when a fact is ready.
972 : 105078 : d_im.doPending();
973 : : // Did we successfully send a lemma? Notice that if hasPending = true
974 : : // and sentLemma = false, then the above call may have:
975 : : // (1) had no pending lemmas, but successfully processed pending facts,
976 : : // (2) unsuccessfully processed pending lemmas.
977 : : // In either case, we repeat the strategy if we are not in conflict.
978 : 105078 : sentLemma = d_im.hasSentLemma();
979 [ - + ]: 105078 : if (TraceIsOn("strings-check"))
980 : : {
981 [ - - ]: 0 : Trace("strings-check") << " ...finish run strategy: ";
982 : 0 : Trace("strings-check") << (hadPending ? "hadPending " : "");
983 : 0 : Trace("strings-check") << (sentLemma ? "sentLemma " : "");
984 : 0 : Trace("strings-check") << (d_state.isInConflict() ? "conflict " : "");
985 [ - - ][ - - ]: 0 : if (!hadPending && !sentLemma && !d_state.isInConflict())
[ - - ][ - - ]
986 : : {
987 [ - - ]: 0 : Trace("strings-check") << "(none)";
988 : : }
989 [ - - ]: 0 : Trace("strings-check") << std::endl;
990 : : }
991 : : // repeat if we did not add a lemma or conflict, and we had pending
992 : : // facts or lemmas.
993 [ + + ][ + + ]: 105078 : } while (!d_state.isInConflict() && !sentLemma && hadPending);
[ + + ][ + + ]
994 : : // End the full effort check.
995 : 69659 : d_termReg.notifyEndFullEffortCheck();
996 : : }
997 [ + - ]: 1829062 : Trace("strings-check") << "Theory of strings, done check : " << e
998 : 914531 : << std::endl;
999 [ - + ][ - + ]: 914531 : Assert(!d_im.hasPendingFact());
[ - - ]
1000 [ - + ][ - + ]: 914531 : Assert(!d_im.hasPendingLemma());
[ - - ]
1001 : 914531 : }
1002 : :
1003 : 26293 : bool TheoryStrings::needsCheckLastEffort()
1004 : : {
1005 [ + - ]: 26293 : if (options().strings.stringModelBasedReduction)
1006 : : {
1007 : 26293 : bool hasExtf = d_esolver.hasExtendedFunctions();
1008 [ + - ]: 52586 : Trace("strings-process")
1009 : 26293 : << "needsCheckLastEffort: hasExtf = " << hasExtf << std::endl;
1010 : 26293 : return hasExtf;
1011 : : }
1012 : 0 : return false;
1013 : : }
1014 : :
1015 : : /** Conflict when merging two constants */
1016 : 3361 : void TheoryStrings::conflict(TNode a, TNode b)
1017 : : {
1018 [ - + ]: 3361 : if (d_state.isInConflict())
1019 : : {
1020 : : // already in conflict
1021 : 0 : return;
1022 : : }
1023 : 3361 : d_im.conflictEqConstantMerge(a, b);
1024 [ + - ]: 3361 : Trace("strings-conflict") << "CONFLICT: Eq engine conflict" << std::endl;
1025 : 3361 : ++(d_statistics.d_conflictsEqEngine);
1026 : : }
1027 : :
1028 : 275565 : void TheoryStrings::eqNotifyNewClass(TNode t)
1029 : : {
1030 : 275565 : Kind k = t.getKind();
1031 [ + + ][ + + ]: 275565 : if (k == Kind::STRING_LENGTH || k == Kind::STRING_TO_CODE)
1032 : : {
1033 [ + - ]: 82079 : Trace("strings-debug") << "New length eqc : " << t << std::endl;
1034 : :
1035 : 82079 : eq::EqualityEngine* ee = d_state.getEqualityEngine();
1036 : 164158 : Node r = ee->getRepresentative(t[0]);
1037 : 82079 : EqcInfo* ei = d_state.getOrMakeEqcInfo(r);
1038 [ + + ]: 82079 : if (k == Kind::STRING_LENGTH)
1039 : : {
1040 : 78484 : ei->d_lengthTerm = t;
1041 : : }
1042 : : else
1043 : : {
1044 : 3595 : ei->d_codeTerm = t[0];
1045 : : }
1046 : 82079 : }
1047 [ + - ]: 275565 : if (d_eagerSolver)
1048 : : {
1049 : 275565 : d_eagerSolver->eqNotifyNewClass(t);
1050 : : }
1051 : 275565 : }
1052 : :
1053 : 2923511 : void TheoryStrings::eqNotifyMerge(TNode t1, TNode t2)
1054 : : {
1055 : 2923511 : EqcInfo* e2 = d_state.getOrMakeEqcInfo(t2, false);
1056 [ + + ]: 2923511 : if (e2 == nullptr)
1057 : : {
1058 : 1303505 : return;
1059 : : }
1060 : : // always create it if e2 was non-null
1061 : 1620006 : EqcInfo* e1 = d_state.getOrMakeEqcInfo(t1);
1062 : :
1063 [ + - ]: 1620006 : if (d_eagerSolver)
1064 : : {
1065 : 1620006 : d_eagerSolver->eqNotifyMerge(e1, t1, e2, t2);
1066 : : }
1067 : :
1068 : : // add information from e2 to e1
1069 [ + + ]: 1620006 : if (!e2->d_lengthTerm.get().isNull())
1070 : : {
1071 : 806597 : e1->d_lengthTerm.set(e2->d_lengthTerm);
1072 : : }
1073 [ + + ]: 1620006 : if (!e2->d_codeTerm.get().isNull())
1074 : : {
1075 : 11769 : e1->d_codeTerm.set(e2->d_codeTerm);
1076 : : }
1077 [ - + ]: 1620006 : if (e2->d_cardinalityLemK.get() > e1->d_cardinalityLemK.get())
1078 : : {
1079 : 0 : e1->d_cardinalityLemK.set(e2->d_cardinalityLemK);
1080 : : }
1081 [ + + ]: 1620006 : if (!e2->d_normalizedLength.get().isNull())
1082 : : {
1083 : 168 : e1->d_normalizedLength.set(e2->d_normalizedLength);
1084 : : }
1085 : : }
1086 : :
1087 : 29087 : void TheoryStrings::computeCareGraph()
1088 : : {
1089 : : // computing the care graph here is probably still necessary, due to operators
1090 : : // that take non-string arguments TODO: verify
1091 [ + - ]: 58174 : Trace("strings-cg")
1092 : 0 : << "TheoryStrings::computeCareGraph(): Build term indices..."
1093 : 29087 : << std::endl;
1094 : : // Term index for each (type, operator) pair. We require the operator here
1095 : : // since operators are polymorphic, taking strings/sequences.
1096 : 29087 : std::map<std::pair<TypeNode, Node>, TNodeTrie> index;
1097 : 29087 : std::map<Node, unsigned> arity;
1098 : 29087 : const context::CDList<TNode>& fterms = d_termReg.getFunctionTerms();
1099 : 29087 : size_t functionTerms = fterms.size();
1100 [ + + ]: 95710 : for (unsigned i = 0; i < functionTerms; ++i)
1101 : : {
1102 : 66623 : TNode f1 = fterms[i];
1103 [ + - ]: 66623 : Trace("strings-cg") << "...build for " << f1 << std::endl;
1104 : 66623 : Node op = f1.getOperator();
1105 : 66623 : std::vector<TNode> reps;
1106 : 66623 : bool has_trigger_arg = false;
1107 [ + + ]: 150947 : for (unsigned j = 0; j < f1.getNumChildren(); j++)
1108 : : {
1109 : 84324 : reps.push_back(d_equalityEngine->getRepresentative(f1[j]));
1110 [ + + ]: 84324 : if (d_equalityEngine->isTriggerTerm(f1[j], THEORY_STRINGS))
1111 : : {
1112 : 57737 : has_trigger_arg = true;
1113 : : }
1114 : : }
1115 [ + + ]: 66623 : if (has_trigger_arg)
1116 : : {
1117 : 46522 : TypeNode ft = utils::getOwnerStringType(f1);
1118 [ - + ][ - - ]: 46522 : AlwaysAssert(ft.isStringLike())
1119 [ - + ][ - + ]: 46522 : << "Unexpected term in getOwnerStringType : " << f1 << ", type "
[ - - ]
1120 : : << ft;
1121 : 46522 : std::pair<TypeNode, Node> ikey = std::pair<TypeNode, Node>(ft, op);
1122 : 46522 : index[ikey].addTerm(f1, reps);
1123 : 46522 : arity[op] = reps.size();
1124 : 46522 : }
1125 : 66623 : }
1126 : : // for each index
1127 [ + + ]: 35601 : for (std::pair<const std::pair<TypeNode, Node>, TNodeTrie>& ti : index)
1128 : : {
1129 [ + - ]: 13028 : Trace("strings-cg") << "TheoryStrings::computeCareGraph(): Process index "
1130 : 6514 : << ti.first << "..." << std::endl;
1131 : 6514 : Node op = ti.first.second;
1132 : 6514 : nodeTriePathPairProcess(&ti.second, arity[op], d_cpacb);
1133 : 6514 : }
1134 : 29087 : }
1135 : :
1136 : 189963 : void TheoryStrings::notifySharedTerm(TNode n)
1137 : : {
1138 : : // a new shared term causes new terms to be relevant, hence we register
1139 : : // them if not doing eager registration.
1140 [ + + ]: 189963 : if (!options().strings.stringEagerReg)
1141 : : {
1142 : 195 : d_termReg.registerSubterms(n);
1143 : : }
1144 : 189963 : TypeNode tn = n.getType();
1145 [ + + ]: 189963 : if (!d_env.isFirstClassType(tn))
1146 : : {
1147 [ - + ][ - + ]: 1 : Assert(tn.isRegExp());
[ - - ]
1148 : 1 : std::stringstream ss;
1149 : 1 : ss << "Regular expression terms are not supported in theory combination";
1150 : 1 : throw LogicException(ss.str());
1151 : 1 : }
1152 : 189963 : }
1153 : :
1154 : 221981 : TrustNode TheoryStrings::ppRewrite(TNode atom, std::vector<SkolemLemma>& lems)
1155 : : {
1156 [ + - ]: 221981 : Trace("strings-ppr") << "TheoryStrings::ppRewrite " << atom << std::endl;
1157 : 221981 : Kind ak = atom.getKind();
1158 [ + + ]: 221981 : if (ak == Kind::STRING_FROM_CODE)
1159 : : {
1160 : : // for the sake of proofs, we use the eager reduction utility
1161 : 99 : Node k = nodeManager()->getSkolemManager()->mkPurifySkolem(atom);
1162 : 99 : TrustNode lemma = d_termReg.eagerReduceTrusted(atom);
1163 : 99 : lems.push_back(SkolemLemma(lemma, k));
1164 : : // We rewrite the term to its purify variable, which can be justified
1165 : : // trivially.
1166 : 99 : return TrustNode::mkTrustRewrite(atom, k, nullptr);
1167 : 99 : }
1168 [ + + ]: 221882 : if (ak == Kind::REGEXP_RANGE)
1169 : : {
1170 [ + + ]: 1150 : for (const Node& nc : atom)
1171 : : {
1172 [ + + ]: 767 : if (!nc.isConst())
1173 : : {
1174 : 1 : throw LogicException(
1175 : 2 : "expecting a constant string term in regexp range");
1176 : : }
1177 [ - + ][ - + ]: 766 : Assert(nc.getConst<String>().size() == 1);
[ - - ]
1178 : 767 : }
1179 : : }
1180 : :
1181 : 221881 : TrustNode ret;
1182 : 221881 : Node atomRet = atom;
1183 : 221881 : if (options().strings.regExpElim != options::RegExpElimMode::OFF
1184 [ + + ][ + + ]: 221881 : && ak == Kind::STRING_IN_REGEXP)
[ + + ]
1185 : : {
1186 : : // aggressive elimination of regular expression membership
1187 : 264 : ret = d_regexp_elim.eliminateTrusted(atomRet);
1188 [ + + ]: 264 : if (!ret.isNull())
1189 : : {
1190 [ + - ][ - + ]: 392 : Trace("strings-ppr") << " rewrote " << atom << " -> " << ret.getNode()
[ - - ]
1191 : 0 : << " via regular expression elimination."
1192 : 196 : << std::endl;
1193 : 196 : atomRet = ret.getNode();
1194 : : }
1195 : : }
1196 [ + + ]: 221881 : if (options().strings.stringFMF)
1197 : : {
1198 : : // Our decision strategy will minimize the length of this term if it is a
1199 : : // variable but not an internally generated Skolem, or a term that does
1200 : : // not belong to this theory.
1201 [ + + ][ + + ]: 29384 : if (atom.isVar() ? !d_termReg.getSkolemCache()->isSkolem(atom)
[ - - ]
1202 : 7040 : : kindToTheoryId(ak) != THEORY_STRINGS
1203 [ - + ][ - - ]: 18212 : && atom.getType().isStringLike())
[ - + ][ + + ]
[ - - ]
1204 : : {
1205 : 532 : d_termReg.preRegisterInputVar(atom);
1206 [ + - ]: 532 : Trace("strings-preregister") << "input variable: " << atom << std::endl;
1207 : : }
1208 : : }
1209 : :
1210 : : // all characters of constants should fall in the alphabet
1211 [ + + ][ + + ]: 221881 : if (atom.isConst() && atom.getType().isString())
[ + + ][ + + ]
[ - - ]
1212 : : {
1213 : 11311 : uint32_t alphaCard = d_termReg.getAlphabetCardinality();
1214 : 11311 : std::vector<unsigned> vec = atom.getConst<String>().getVec();
1215 [ + + ]: 37052 : for (unsigned u : vec)
1216 : : {
1217 [ - + ]: 25741 : if (u >= alphaCard)
1218 : : {
1219 : 0 : std::stringstream ss;
1220 : 0 : ss << "Characters in string \"" << atom
1221 : 0 : << "\" are outside of the given alphabet.";
1222 : 0 : throw LogicException(ss.str());
1223 : 0 : }
1224 : : }
1225 : 11311 : }
1226 [ - + ]: 221881 : if (!options().strings.stringExp)
1227 : : {
1228 [ - - ][ - - ]: 0 : if (ak == Kind::STRING_INDEXOF || ak == Kind::STRING_INDEXOF_RE
1229 [ - - ][ - - ]: 0 : || ak == Kind::STRING_ITOS || ak == Kind::STRING_STOI
1230 [ - - ][ - - ]: 0 : || ak == Kind::STRING_REPLACE || ak == Kind::STRING_SUBSTR
1231 [ - - ][ - - ]: 0 : || ak == Kind::STRING_REPLACE_ALL || ak == Kind::SEQ_NTH
1232 [ - - ][ - - ]: 0 : || ak == Kind::STRING_REPLACE_RE || ak == Kind::STRING_REPLACE_RE_ALL
1233 [ - - ][ - - ]: 0 : || ak == Kind::STRING_CONTAINS || ak == Kind::STRING_LEQ
1234 [ - - ][ - - ]: 0 : || ak == Kind::STRING_TO_LOWER || ak == Kind::STRING_TO_UPPER
1235 [ - - ][ - - ]: 0 : || ak == Kind::STRING_REV || ak == Kind::STRING_UPDATE)
1236 : : {
1237 : 0 : std::stringstream ss;
1238 : 0 : ss << "Term of kind " << printer::smt2::Smt2Printer::smtKindStringOf(atom)
1239 : 0 : << " not supported in default mode, try --strings-exp";
1240 : 0 : throw LogicException(ss.str());
1241 : 0 : }
1242 : : }
1243 : 221881 : return ret;
1244 : 221881 : }
1245 : :
1246 : 30271 : TrustNode TheoryStrings::ppStaticRewrite(TNode atom)
1247 : : {
1248 : 30271 : Kind ak = atom.getKind();
1249 [ + + ]: 30271 : if (ak == Kind::EQUAL)
1250 : : {
1251 [ + + ]: 5624 : if (atom[0].getType().isRegExp())
1252 : : {
1253 : 10 : Node res = d_rewriter.rewriteViaRule(ProofRewriteRule::RE_EQ_ELIM, atom);
1254 [ - + ][ - + ]: 10 : Assert(!res.isNull());
[ - - ]
1255 [ + + ]: 10 : return TrustNode::mkTrustRewrite(atom, res, d_psrewPg.get());
1256 : 10 : }
1257 : : // always apply aggressive equality rewrites here
1258 : 5614 : Node ret = d_rewriter.rewriteEqualityExt(atom);
1259 [ + + ]: 5614 : if (ret != atom)
1260 : : {
1261 [ + + ]: 688 : return TrustNode::mkTrustRewrite(atom, ret, d_psrewPg.get());
1262 : : }
1263 [ + + ]: 5614 : }
1264 : 29573 : return TrustNode::null();
1265 : : }
1266 : :
1267 : : /** run the given inference step */
1268 : 979464 : void TheoryStrings::runInferStep(InferStep s, Theory::Effort e, int effort)
1269 : : {
1270 [ + - ]: 979464 : Trace("strings-process") << "Run " << s;
1271 [ + + ]: 979464 : if (effort > 0)
1272 : : {
1273 [ + - ]: 47940 : Trace("strings-process") << ", effort = " << effort;
1274 : : }
1275 [ + - ]: 979464 : Trace("strings-process") << "..." << std::endl;
1276 [ + + ][ + + ]: 979464 : switch (s)
[ + + ][ + + ]
[ + + ][ + + ]
[ + - ][ + + ]
[ + + ][ + - ]
1277 : : {
1278 : 103991 : case InferStep::CHECK_INIT: d_bsolver.checkInit(); break;
1279 : 85481 : case InferStep::CHECK_CONST_EQC:
1280 : 85481 : d_bsolver.checkConstantEquivalenceClasses();
1281 : 85481 : break;
1282 : 133365 : case InferStep::CHECK_EXTF_EVAL: d_esolver.checkExtfEval(effort); break;
1283 : 80672 : case InferStep::CHECK_CYCLES: d_csolver.checkCycles(); break;
1284 : 80636 : case InferStep::CHECK_FLAT_FORMS: d_csolver.checkFlatForms(); break;
1285 : 65366 : case InferStep::CHECK_NORMAL_FORMS_EQ_PROP:
1286 : 65366 : d_csolver.checkNormalFormsEqProp();
1287 : 65366 : break;
1288 : 53191 : case InferStep::CHECK_NORMAL_FORMS_EQ:
1289 : 53191 : d_csolver.checkNormalFormsEq();
1290 : 53191 : break;
1291 : 43305 : case InferStep::CHECK_NORMAL_FORMS_DEQ:
1292 : 43305 : d_csolver.checkNormalFormsDeq();
1293 : 43305 : break;
1294 : 42319 : case InferStep::CHECK_CODES: d_psolver.checkCodes(); break;
1295 : 42125 : case InferStep::CHECK_LENGTH_EQC: d_csolver.checkLengthsEqc(); break;
1296 : 389 : case InferStep::CHECK_SEQUENCES_ARRAY_CONCAT:
1297 : 389 : d_asolver.checkArrayConcat();
1298 : 389 : break;
1299 : 245 : case InferStep::CHECK_SEQUENCES_ARRAY: d_asolver.checkArray(); break;
1300 : 170 : case InferStep::CHECK_SEQUENCES_ARRAY_EAGER:
1301 : 170 : d_asolver.checkArrayEager();
1302 : 170 : break;
1303 : 0 : case InferStep::CHECK_REGISTER_TERMS_NF:
1304 : 0 : d_csolver.checkRegisterTermsNormalForms();
1305 : 0 : break;
1306 : 68628 : case InferStep::CHECK_EXTF_REDUCTION_EAGER:
1307 : 68628 : d_esolver.checkExtfReductionsEager();
1308 : 68628 : break;
1309 : 39891 : case InferStep::CHECK_EXTF_REDUCTION:
1310 : 39891 : d_esolver.checkExtfReductions(e);
1311 : 39891 : break;
1312 : 65373 : case InferStep::CHECK_MEMBERSHIP_EAGER:
1313 : 65373 : d_rsolver.checkMembershipsEager();
1314 : 65373 : break;
1315 : 37977 : case InferStep::CHECK_MEMBERSHIP: d_rsolver.checkMemberships(e); break;
1316 : 36340 : case InferStep::CHECK_CARDINALITY: d_bsolver.checkCardinality(); break;
1317 : 0 : default: Unreachable(); break;
1318 : : }
1319 [ + - ]: 1958928 : Trace("strings-process") << "Done " << s
1320 : 0 : << ", addedFact = " << d_im.hasPendingFact()
1321 : 0 : << ", addedLemma = " << d_im.hasPendingLemma()
1322 : 979464 : << ", conflict = " << d_state.isInConflict()
1323 : 979464 : << std::endl;
1324 : 979464 : }
1325 : :
1326 : 105078 : void TheoryStrings::runStrategy(Theory::Effort e)
1327 : : {
1328 : 105078 : std::vector<std::pair<InferStep, int>>::iterator it = d_strat.stepBegin(e);
1329 : 105078 : std::vector<std::pair<InferStep, int>>::iterator stepEnd = d_strat.stepEnd(e);
1330 : :
1331 [ + - ]: 105078 : Trace("strings-process") << "----check, next round---" << std::endl;
1332 [ + + ]: 1956469 : while (it != stepEnd)
1333 : : {
1334 : 1919059 : InferStep curr = it->first;
1335 : 1919059 : int effort = it->second;
1336 [ + + ]: 1919059 : if (curr == InferStep::BREAK)
1337 : : {
1338 : : // if we have a pending inference or lemma, we will process it
1339 [ + + ]: 939595 : if (d_im.hasProcessed())
1340 : : {
1341 : 65209 : break;
1342 : : }
1343 : : }
1344 : : else
1345 : : {
1346 : 979464 : runInferStep(curr, e, effort);
1347 [ + + ]: 979464 : if (d_state.isInConflict())
1348 : : {
1349 : 2459 : break;
1350 : : }
1351 : : }
1352 : 1851391 : ++it;
1353 : : }
1354 [ + - ]: 105078 : Trace("strings-process") << "----finished round---" << std::endl;
1355 : 105078 : }
1356 : :
1357 : 0 : std::string TheoryStrings::debugPrintStringsEqc()
1358 : : {
1359 : 0 : std::stringstream ss;
1360 [ - - ]: 0 : for (unsigned t = 0; t < 2; t++)
1361 : : {
1362 : 0 : eq::EqClassesIterator eqcs2_i = eq::EqClassesIterator(d_equalityEngine);
1363 [ - - ]: 0 : ss << (t == 0 ? "STRINGS:" : "OTHER:") << std::endl;
1364 [ - - ]: 0 : while (!eqcs2_i.isFinished())
1365 : : {
1366 : 0 : Node eqc = (*eqcs2_i);
1367 : 0 : bool print = (t == 0 && eqc.getType().isStringLike())
1368 : 0 : || (t == 1 && !eqc.getType().isStringLike());
1369 [ - - ]: 0 : if (print)
1370 : : {
1371 : 0 : eq::EqClassIterator eqc2_i = eq::EqClassIterator(eqc, d_equalityEngine);
1372 : 0 : ss << "Eqc( " << eqc << " ) : { ";
1373 [ - - ]: 0 : while (!eqc2_i.isFinished())
1374 : : {
1375 : 0 : if ((*eqc2_i) != eqc && (*eqc2_i).getKind() != Kind::EQUAL)
1376 : : {
1377 : 0 : ss << (*eqc2_i) << " ";
1378 : : }
1379 : 0 : ++eqc2_i;
1380 : : }
1381 : 0 : ss << " } " << std::endl;
1382 : 0 : EqcInfo* ei = d_state.getOrMakeEqcInfo(eqc, false);
1383 [ - - ]: 0 : if (ei)
1384 : : {
1385 [ - - ]: 0 : Trace("strings-eqc-debug")
1386 : 0 : << "* Length term : " << ei->d_lengthTerm.get() << std::endl;
1387 [ - - ]: 0 : Trace("strings-eqc-debug")
1388 : 0 : << "* Cardinality lemma k : " << ei->d_cardinalityLemK.get()
1389 : 0 : << std::endl;
1390 [ - - ]: 0 : Trace("strings-eqc-debug")
1391 : 0 : << "* Normalization length lemma : "
1392 : 0 : << ei->d_normalizedLength.get() << std::endl;
1393 : : }
1394 : : }
1395 : 0 : ++eqcs2_i;
1396 : 0 : }
1397 : 0 : ss << std::endl;
1398 : : }
1399 : 0 : ss << std::endl;
1400 : 0 : return ss.str();
1401 : 0 : }
1402 : :
1403 : : } // namespace strings
1404 : : } // namespace theory
1405 : : } // namespace cvc5::internal
|