ruby-minisat 1.14.2 → 2.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +18 -21
- data/Gemfile +4 -0
- data/LICENSE +3 -2
- data/README.md +42 -0
- data/Rakefile +1 -48
- data/examples/shikaku.rb +2 -2
- data/ext/minisat/extconf.rb +19 -4
- data/ext/minisat/minisat-wrap.cpp +11 -5
- data/ext/minisat/minisat.c +17 -3
- data/ext/minisat/minisat.h +1 -1
- data/minisat/minisat-2.2.0.tar.gz +0 -0
- data/minisat/{MiniSat_v1.14 → minisat}/LICENSE +2 -1
- data/minisat/minisat/README +24 -0
- data/minisat/minisat/core/Dimacs.h +89 -0
- data/minisat/minisat/core/Main.cc +192 -0
- data/minisat/minisat/core/Solver.cc +923 -0
- data/minisat/minisat/core/Solver.h +373 -0
- data/minisat/minisat/core/SolverTypes.h +407 -0
- data/minisat/minisat/mtl/Alg.h +84 -0
- data/minisat/minisat/mtl/Alloc.h +131 -0
- data/minisat/minisat/mtl/Heap.h +148 -0
- data/minisat/minisat/mtl/IntTypes.h +42 -0
- data/minisat/minisat/mtl/Map.h +193 -0
- data/minisat/minisat/mtl/Queue.h +69 -0
- data/minisat/{MiniSat_v1.14 → minisat/mtl}/Sort.h +14 -47
- data/minisat/minisat/mtl/Vec.h +130 -0
- data/minisat/minisat/mtl/XAlloc.h +45 -0
- data/minisat/minisat/mtl/config.mk +6 -0
- data/minisat/minisat/mtl/template.mk +107 -0
- data/minisat/minisat/simp/Main.cc +211 -0
- data/minisat/minisat/simp/SimpSolver.cc +717 -0
- data/minisat/minisat/simp/SimpSolver.h +197 -0
- data/minisat/minisat/utils/Options.cc +91 -0
- data/minisat/minisat/utils/Options.h +386 -0
- data/minisat/minisat/utils/ParseUtils.h +122 -0
- data/minisat/minisat/utils/System.cc +95 -0
- data/minisat/minisat/utils/System.h +60 -0
- data/ruby-minisat.gemspec +21 -0
- metadata +94 -75
- data/README.rdoc +0 -56
- data/minisat/MiniSat_v1.14.2006-Aug-29.src.zip +0 -0
- data/minisat/MiniSat_v1.14/Global.h +0 -274
- data/minisat/MiniSat_v1.14/Heap.h +0 -100
- data/minisat/MiniSat_v1.14/Main.C +0 -244
- data/minisat/MiniSat_v1.14/Makefile +0 -88
- data/minisat/MiniSat_v1.14/README +0 -30
- data/minisat/MiniSat_v1.14/Solver.C +0 -781
- data/minisat/MiniSat_v1.14/Solver.h +0 -206
- data/minisat/MiniSat_v1.14/Solver.o +0 -0
- data/minisat/MiniSat_v1.14/SolverTypes.h +0 -130
- data/minisat/MiniSat_v1.14/TODO +0 -73
- data/minisat/MiniSat_v1.14/VarOrder.h +0 -96
@@ -1,88 +0,0 @@
|
|
1
|
-
##
|
2
|
-
## Makefile for Standard, Profile, Debug, Release, and Release-static versions of MiniSat
|
3
|
-
##
|
4
|
-
## eg: "make rs" for a statically linked release version.
|
5
|
-
## "make d" for a debug version (no optimizations).
|
6
|
-
## "make" for the standard version (optimized, but with debug information and assertions active)
|
7
|
-
|
8
|
-
CSRCS = $(wildcard *.C)
|
9
|
-
CHDRS = $(wildcard *.h)
|
10
|
-
COBJS = $(addsuffix .o, $(basename $(CSRCS)))
|
11
|
-
|
12
|
-
PCOBJS = $(addsuffix p, $(COBJS))
|
13
|
-
DCOBJS = $(addsuffix d, $(COBJS))
|
14
|
-
RCOBJS = $(addsuffix r, $(COBJS))
|
15
|
-
|
16
|
-
EXEC = minisat
|
17
|
-
|
18
|
-
CXX = g++
|
19
|
-
CFLAGS = -Wall -ffloat-store
|
20
|
-
COPTIMIZE = -O3
|
21
|
-
|
22
|
-
|
23
|
-
.PHONY : s p d r build clean depend
|
24
|
-
|
25
|
-
s: WAY=standard
|
26
|
-
p: WAY=profile
|
27
|
-
d: WAY=debug
|
28
|
-
r: WAY=release
|
29
|
-
rs: WAY=release static
|
30
|
-
|
31
|
-
s: CFLAGS+=$(COPTIMIZE) -ggdb -D DEBUG
|
32
|
-
p: CFLAGS+=$(COPTIMIZE) -pg -ggdb -D DEBUG
|
33
|
-
d: CFLAGS+=-O0 -ggdb -D DEBUG
|
34
|
-
r: CFLAGS+=$(COPTIMIZE) -D NDEBUG
|
35
|
-
rs: CFLAGS+=$(COPTIMIZE) -D NDEBUG
|
36
|
-
|
37
|
-
s: build $(EXEC)
|
38
|
-
p: build $(EXEC)_profile
|
39
|
-
d: build $(EXEC)_debug
|
40
|
-
r: build $(EXEC)_release
|
41
|
-
rs: build $(EXEC)_static
|
42
|
-
|
43
|
-
build:
|
44
|
-
@echo Building $(EXEC) "("$(WAY)")"
|
45
|
-
|
46
|
-
clean:
|
47
|
-
@rm -f $(EXEC) $(EXEC)_profile $(EXEC)_debug $(EXEC)_release $(EXEC)_static \
|
48
|
-
$(COBJS) $(PCOBJS) $(DCOBJS) $(RCOBJS) depend.mak
|
49
|
-
|
50
|
-
## Build rule
|
51
|
-
%.o %.op %.od %.or: %.C
|
52
|
-
@echo Compiling: $<
|
53
|
-
@$(CXX) $(CFLAGS) -c -o $@ $<
|
54
|
-
|
55
|
-
## Linking rules (standard/profile/debug/release)
|
56
|
-
$(EXEC): $(COBJS)
|
57
|
-
@echo Linking $(EXEC)
|
58
|
-
@$(CXX) $(COBJS) -lz -ggdb -Wall -o $@
|
59
|
-
|
60
|
-
$(EXEC)_profile: $(PCOBJS)
|
61
|
-
@echo Linking $@
|
62
|
-
@$(CXX) $(PCOBJS) -lz -ggdb -Wall -pg -o $@
|
63
|
-
|
64
|
-
$(EXEC)_debug: $(DCOBJS)
|
65
|
-
@echo Linking $@
|
66
|
-
@$(CXX) $(DCOBJS) -lz -ggdb -Wall -o $@
|
67
|
-
|
68
|
-
$(EXEC)_release: $(RCOBJS)
|
69
|
-
@echo Linking $@
|
70
|
-
@$(CXX) $(RCOBJS) -lz -Wall -o $@
|
71
|
-
|
72
|
-
$(EXEC)_static: $(RCOBJS)
|
73
|
-
@echo Linking $@
|
74
|
-
@$(CXX) --static $(RCOBJS) -lz -Wall -o $@
|
75
|
-
|
76
|
-
|
77
|
-
## Make dependencies
|
78
|
-
depend: depend.mak
|
79
|
-
depend.mak: $(CSRCS) $(CHDRS)
|
80
|
-
@echo Making dependencies ...
|
81
|
-
@$(CXX) -MM $(CSRCS) > depend.mak
|
82
|
-
@cp depend.mak /tmp/depend.mak.tmp
|
83
|
-
@sed "s/o:/op:/" /tmp/depend.mak.tmp >> depend.mak
|
84
|
-
@sed "s/o:/od:/" /tmp/depend.mak.tmp >> depend.mak
|
85
|
-
@sed "s/o:/or:/" /tmp/depend.mak.tmp >> depend.mak
|
86
|
-
@rm /tmp/depend.mak.tmp
|
87
|
-
|
88
|
-
include depend.mak
|
@@ -1,30 +0,0 @@
|
|
1
|
-
MiniSat v1.14 / MiniSat-p v1.14
|
2
|
-
========================================
|
3
|
-
|
4
|
-
This version is a cleaned up version of the MiniSat solver entering
|
5
|
-
the SAT 2005 competition. Some of the most low-level optimization has
|
6
|
-
been removed for the sake of clarity, resulting in a 5-10% performance
|
7
|
-
degradation. The guard on "too-many-calls" in 'simplifyDB()' has also
|
8
|
-
been improved. If uttermost performance is needed, use the competition
|
9
|
-
version, also available at the MiniSat page
|
10
|
-
(www.cs.chalmers.se/Cs/Research/FormalMethods/MiniSat/).
|
11
|
-
|
12
|
-
Several things has changed inside MiniSat since the first version,
|
13
|
-
documented in the paper "An Extensible SAT Solver". The most important
|
14
|
-
is the lack of support for non-clausal constraints in this
|
15
|
-
version. Please use MiniSat v1.12b if you need this. Other changes are
|
16
|
-
special treatment of binary clauses for efficiency (messes up the code
|
17
|
-
a bit, but gives quite a speedup), improved variable order (still
|
18
|
-
VSIDS based, but works MUCH better than in previous MiniSat
|
19
|
-
versions!), conflict clause minimization (by Niklas S�rensson), and a
|
20
|
-
better Main module (for those of you who use MiniSat as a stand-alone
|
21
|
-
solver).
|
22
|
-
|
23
|
-
The MiniSat-p version also supports proof logging, sacrificing the
|
24
|
-
binary clauses trick for clarity (some 10-20% slower). For
|
25
|
-
scalability, we decided to keep the proof only on disk. This frees up
|
26
|
-
memory for the working set of conflict clauses, and allows for
|
27
|
-
bigger-than-memory proofs to be produced (which can happen, even if
|
28
|
-
you garbage collect the proof).
|
29
|
-
|
30
|
-
For information about upcomming changes, please review the TODO file.
|
@@ -1,781 +0,0 @@
|
|
1
|
-
/****************************************************************************************[Solver.C]
|
2
|
-
MiniSat -- Copyright (c) 2003-2005, Niklas Een, Niklas Sorensson
|
3
|
-
|
4
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
|
5
|
-
associated documentation files (the "Software"), to deal in the Software without restriction,
|
6
|
-
including without limitation the rights to use, copy, modify, merge, publish, distribute,
|
7
|
-
sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
|
8
|
-
furnished to do so, subject to the following conditions:
|
9
|
-
|
10
|
-
The above copyright notice and this permission notice shall be included in all copies or
|
11
|
-
substantial portions of the Software.
|
12
|
-
|
13
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
|
14
|
-
NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
15
|
-
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
16
|
-
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT
|
17
|
-
OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
18
|
-
**************************************************************************************************/
|
19
|
-
|
20
|
-
#include "Solver.h"
|
21
|
-
#include "Sort.h"
|
22
|
-
#include <cmath>
|
23
|
-
|
24
|
-
|
25
|
-
//=================================================================================================
|
26
|
-
// Helper functions:
|
27
|
-
|
28
|
-
|
29
|
-
bool removeWatch(vec<GClause>& ws, GClause elem) // Pre-condition: 'elem' must exists in 'ws' OR 'ws' must be empty.
|
30
|
-
{
|
31
|
-
if (ws.size() == 0) return false; // (skip lists that are already cleared)
|
32
|
-
int j = 0;
|
33
|
-
for (; ws[j] != elem ; j++) assert(j < ws.size());
|
34
|
-
for (; j < ws.size()-1; j++) ws[j] = ws[j+1];
|
35
|
-
ws.pop();
|
36
|
-
return true;
|
37
|
-
}
|
38
|
-
|
39
|
-
|
40
|
-
//=================================================================================================
|
41
|
-
// Operations on clauses:
|
42
|
-
|
43
|
-
|
44
|
-
/*_________________________________________________________________________________________________
|
45
|
-
|
|
46
|
-
| newClause : (ps : const vec<Lit>&) (learnt : bool) -> [void]
|
47
|
-
|
|
48
|
-
| Description:
|
49
|
-
| Allocate and add a new clause to the SAT solvers clause database. If a conflict is detected,
|
50
|
-
| the 'ok' flag is cleared and the solver is in an unusable state (must be disposed).
|
51
|
-
|
|
52
|
-
| Input:
|
53
|
-
| ps - The new clause as a vector of literals.
|
54
|
-
| learnt - Is the clause a learnt clause? For learnt clauses, 'ps[0]' is assumed to be the
|
55
|
-
| asserting literal. An appropriate 'enqueue()' operation will be performed on this
|
56
|
-
| literal. One of the watches will always be on this literal, the other will be set to
|
57
|
-
| the literal with the highest decision level.
|
58
|
-
|
|
59
|
-
| Effect:
|
60
|
-
| Activity heuristics are updated.
|
61
|
-
|________________________________________________________________________________________________@*/
|
62
|
-
void Solver::newClause(const vec<Lit>& ps_, bool learnt)
|
63
|
-
{
|
64
|
-
if (!ok) return;
|
65
|
-
|
66
|
-
vec<Lit> qs;
|
67
|
-
if (!learnt){
|
68
|
-
assert(decisionLevel() == 0);
|
69
|
-
ps_.copyTo(qs); // Make a copy of the input vector.
|
70
|
-
|
71
|
-
// Remove duplicates:
|
72
|
-
sortUnique(qs);
|
73
|
-
|
74
|
-
// Check if clause is satisfied:
|
75
|
-
for (int i = 0; i < qs.size()-1; i++){
|
76
|
-
if (qs[i] == ~qs[i+1])
|
77
|
-
return; }
|
78
|
-
for (int i = 0; i < qs.size(); i++){
|
79
|
-
if (value(qs[i]) == l_True)
|
80
|
-
return; }
|
81
|
-
|
82
|
-
// Remove false literals:
|
83
|
-
int i, j;
|
84
|
-
for (i = j = 0; i < qs.size(); i++)
|
85
|
-
if (value(qs[i]) != l_False)
|
86
|
-
qs[j++] = qs[i];
|
87
|
-
qs.shrink(i - j);
|
88
|
-
}
|
89
|
-
const vec<Lit>& ps = learnt ? ps_ : qs; // 'ps' is now the (possibly) reduced vector of literals.
|
90
|
-
|
91
|
-
if (ps.size() == 0){
|
92
|
-
ok = false;
|
93
|
-
|
94
|
-
}else if (ps.size() == 1){
|
95
|
-
// NOTE: If enqueue takes place at root level, the assignment will be lost in incremental use (it doesn't seem to hurt much though).
|
96
|
-
if (!enqueue(ps[0]))
|
97
|
-
ok = false;
|
98
|
-
|
99
|
-
}else if (ps.size() == 2){
|
100
|
-
// Create special binary clause watch:
|
101
|
-
watches[index(~ps[0])].push(GClause_new(ps[1]));
|
102
|
-
watches[index(~ps[1])].push(GClause_new(ps[0]));
|
103
|
-
|
104
|
-
if (learnt){
|
105
|
-
check(enqueue(ps[0], GClause_new(~ps[1])));
|
106
|
-
stats.learnts_literals += ps.size();
|
107
|
-
}else
|
108
|
-
stats.clauses_literals += ps.size();
|
109
|
-
n_bin_clauses++;
|
110
|
-
|
111
|
-
}else{
|
112
|
-
// Allocate clause:
|
113
|
-
Clause* c = Clause_new(learnt, ps);
|
114
|
-
|
115
|
-
if (learnt){
|
116
|
-
// Put the second watch on the literal with highest decision level:
|
117
|
-
int max_i = 1;
|
118
|
-
int max = level[var(ps[1])];
|
119
|
-
for (int i = 2; i < ps.size(); i++)
|
120
|
-
if (level[var(ps[i])] > max)
|
121
|
-
max = level[var(ps[i])],
|
122
|
-
max_i = i;
|
123
|
-
(*c)[1] = ps[max_i];
|
124
|
-
(*c)[max_i] = ps[1];
|
125
|
-
|
126
|
-
// Bump, enqueue, store clause:
|
127
|
-
claBumpActivity(c); // (newly learnt clauses should be considered active)
|
128
|
-
check(enqueue((*c)[0], GClause_new(c)));
|
129
|
-
learnts.push(c);
|
130
|
-
stats.learnts_literals += c->size();
|
131
|
-
}else{
|
132
|
-
// Store clause:
|
133
|
-
clauses.push(c);
|
134
|
-
stats.clauses_literals += c->size();
|
135
|
-
}
|
136
|
-
// Watch clause:
|
137
|
-
watches[index(~(*c)[0])].push(GClause_new(c));
|
138
|
-
watches[index(~(*c)[1])].push(GClause_new(c));
|
139
|
-
}
|
140
|
-
}
|
141
|
-
|
142
|
-
|
143
|
-
// Disposes a clauses and removes it from watcher lists. NOTE! Low-level; does NOT change the 'clauses' and 'learnts' vector.
|
144
|
-
//
|
145
|
-
void Solver::remove(Clause* c, bool just_dealloc)
|
146
|
-
{
|
147
|
-
if (!just_dealloc){
|
148
|
-
if (c->size() == 2)
|
149
|
-
removeWatch(watches[index(~(*c)[0])], GClause_new((*c)[1])),
|
150
|
-
removeWatch(watches[index(~(*c)[1])], GClause_new((*c)[0]));
|
151
|
-
else
|
152
|
-
removeWatch(watches[index(~(*c)[0])], GClause_new(c)),
|
153
|
-
removeWatch(watches[index(~(*c)[1])], GClause_new(c));
|
154
|
-
}
|
155
|
-
|
156
|
-
if (c->learnt()) stats.learnts_literals -= c->size();
|
157
|
-
else stats.clauses_literals -= c->size();
|
158
|
-
|
159
|
-
xfree(c);
|
160
|
-
}
|
161
|
-
|
162
|
-
|
163
|
-
// Can assume everything has been propagated! (esp. the first two literals are != l_False, unless
|
164
|
-
// the clause is binary and satisfied, in which case the first literal is true)
|
165
|
-
// Returns True if clause is satisfied (will be removed), False otherwise.
|
166
|
-
//
|
167
|
-
bool Solver::simplify(Clause* c) const
|
168
|
-
{
|
169
|
-
assert(decisionLevel() == 0);
|
170
|
-
for (int i = 0; i < c->size(); i++){
|
171
|
-
if (value((*c)[i]) == l_True)
|
172
|
-
return true;
|
173
|
-
}
|
174
|
-
return false;
|
175
|
-
}
|
176
|
-
|
177
|
-
|
178
|
-
//=================================================================================================
|
179
|
-
// Minor methods:
|
180
|
-
|
181
|
-
|
182
|
-
// Creates a new SAT variable in the solver. If 'decision_var' is cleared, variable will not be
|
183
|
-
// used as a decision variable (NOTE! This has effects on the meaning of a SATISFIABLE result).
|
184
|
-
//
|
185
|
-
Var Solver::newVar() {
|
186
|
-
int index;
|
187
|
-
index = nVars();
|
188
|
-
watches .push(); // (list for positive literal)
|
189
|
-
watches .push(); // (list for negative literal)
|
190
|
-
reason .push(GClause_NULL);
|
191
|
-
assigns .push(toInt(l_Undef));
|
192
|
-
level .push(-1);
|
193
|
-
activity .push(0);
|
194
|
-
order .newVar();
|
195
|
-
analyze_seen.push(0);
|
196
|
-
return index; }
|
197
|
-
|
198
|
-
|
199
|
-
// Returns FALSE if immediate conflict.
|
200
|
-
bool Solver::assume(Lit p) {
|
201
|
-
trail_lim.push(trail.size());
|
202
|
-
return enqueue(p); }
|
203
|
-
|
204
|
-
|
205
|
-
// Revert to the state at given level.
|
206
|
-
void Solver::cancelUntil(int level) {
|
207
|
-
if (decisionLevel() > level){
|
208
|
-
for (int c = trail.size()-1; c >= trail_lim[level]; c--){
|
209
|
-
Var x = var(trail[c]);
|
210
|
-
assigns[x] = toInt(l_Undef);
|
211
|
-
reason [x] = GClause_NULL;
|
212
|
-
order.undo(x); }
|
213
|
-
trail.shrink(trail.size() - trail_lim[level]);
|
214
|
-
trail_lim.shrink(trail_lim.size() - level);
|
215
|
-
qhead = trail.size(); } }
|
216
|
-
|
217
|
-
|
218
|
-
//=================================================================================================
|
219
|
-
// Major methods:
|
220
|
-
|
221
|
-
|
222
|
-
/*_________________________________________________________________________________________________
|
223
|
-
|
|
224
|
-
| analyze : (confl : Clause*) (out_learnt : vec<Lit>&) (out_btlevel : int&) -> [void]
|
225
|
-
|
|
226
|
-
| Description:
|
227
|
-
| Analyze conflict and produce a reason clause.
|
228
|
-
|
|
229
|
-
| Pre-conditions:
|
230
|
-
| * 'out_learnt' is assumed to be cleared.
|
231
|
-
| * Current decision level must be greater than root level.
|
232
|
-
|
|
233
|
-
| Post-conditions:
|
234
|
-
| * 'out_learnt[0]' is the asserting literal at level 'out_btlevel'.
|
235
|
-
|
|
236
|
-
| Effect:
|
237
|
-
| Will undo part of the trail, upto but not beyond the assumption of the current decision level.
|
238
|
-
|________________________________________________________________________________________________@*/
|
239
|
-
void Solver::analyze(Clause* _confl, vec<Lit>& out_learnt, int& out_btlevel)
|
240
|
-
{
|
241
|
-
GClause confl = GClause_new(_confl);
|
242
|
-
vec<char>& seen = analyze_seen;
|
243
|
-
int pathC = 0;
|
244
|
-
Lit p = lit_Undef;
|
245
|
-
|
246
|
-
// Generate conflict clause:
|
247
|
-
//
|
248
|
-
out_learnt.push(); // (leave room for the asserting literal)
|
249
|
-
out_btlevel = 0;
|
250
|
-
int index = trail.size()-1;
|
251
|
-
do{
|
252
|
-
assert(confl != GClause_NULL); // (otherwise should be UIP)
|
253
|
-
|
254
|
-
Clause& c = confl.isLit() ? ((*analyze_tmpbin)[1] = confl.lit(), *analyze_tmpbin)
|
255
|
-
: *confl.clause();
|
256
|
-
if (c.learnt())
|
257
|
-
claBumpActivity(&c);
|
258
|
-
|
259
|
-
for (int j = (p == lit_Undef) ? 0 : 1; j < c.size(); j++){
|
260
|
-
Lit q = c[j];
|
261
|
-
if (!seen[var(q)] && level[var(q)] > 0){
|
262
|
-
varBumpActivity(q);
|
263
|
-
seen[var(q)] = 1;
|
264
|
-
if (level[var(q)] == decisionLevel())
|
265
|
-
pathC++;
|
266
|
-
else{
|
267
|
-
out_learnt.push(q);
|
268
|
-
out_btlevel = max(out_btlevel, level[var(q)]);
|
269
|
-
}
|
270
|
-
}
|
271
|
-
}
|
272
|
-
|
273
|
-
// Select next clause to look at:
|
274
|
-
while (!seen[var(trail[index--])]);
|
275
|
-
p = trail[index+1];
|
276
|
-
confl = reason[var(p)];
|
277
|
-
seen[var(p)] = 0;
|
278
|
-
pathC--;
|
279
|
-
|
280
|
-
}while (pathC > 0);
|
281
|
-
out_learnt[0] = ~p;
|
282
|
-
|
283
|
-
int i, j;
|
284
|
-
if (expensive_ccmin){
|
285
|
-
// Simplify conflict clause (a lot):
|
286
|
-
//
|
287
|
-
uint min_level = 0;
|
288
|
-
for (i = 1; i < out_learnt.size(); i++)
|
289
|
-
min_level |= 1 << (level[var(out_learnt[i])] & 31); // (maintain an abstraction of levels involved in conflict)
|
290
|
-
|
291
|
-
out_learnt.copyTo(analyze_toclear);
|
292
|
-
for (i = j = 1; i < out_learnt.size(); i++)
|
293
|
-
if (reason[var(out_learnt[i])] == GClause_NULL || !analyze_removable(out_learnt[i], min_level))
|
294
|
-
out_learnt[j++] = out_learnt[i];
|
295
|
-
}else{
|
296
|
-
// Simplify conflict clause (a little):
|
297
|
-
//
|
298
|
-
out_learnt.copyTo(analyze_toclear);
|
299
|
-
for (i = j = 1; i < out_learnt.size(); i++){
|
300
|
-
GClause r = reason[var(out_learnt[i])];
|
301
|
-
if (r == GClause_NULL)
|
302
|
-
out_learnt[j++] = out_learnt[i];
|
303
|
-
else if (r.isLit()){
|
304
|
-
Lit q = r.lit();
|
305
|
-
if (!seen[var(q)] && level[var(q)] != 0)
|
306
|
-
out_learnt[j++] = out_learnt[i];
|
307
|
-
}else{
|
308
|
-
Clause& c = *r.clause();
|
309
|
-
for (int k = 1; k < c.size(); k++)
|
310
|
-
if (!seen[var(c[k])] && level[var(c[k])] != 0){
|
311
|
-
out_learnt[j++] = out_learnt[i];
|
312
|
-
break; }
|
313
|
-
}
|
314
|
-
}
|
315
|
-
}
|
316
|
-
|
317
|
-
stats.max_literals += out_learnt.size();
|
318
|
-
out_learnt.shrink(i - j);
|
319
|
-
stats.tot_literals += out_learnt.size();
|
320
|
-
|
321
|
-
for (int j = 0; j < analyze_toclear.size(); j++) seen[var(analyze_toclear[j])] = 0; // ('seen[]' is now cleared)
|
322
|
-
}
|
323
|
-
|
324
|
-
|
325
|
-
// Check if 'p' can be removed. 'min_level' is used to abort early if visiting literals at a level that cannot be removed.
|
326
|
-
//
|
327
|
-
bool Solver::analyze_removable(Lit p, uint min_level)
|
328
|
-
{
|
329
|
-
assert(reason[var(p)] != GClause_NULL);
|
330
|
-
analyze_stack.clear(); analyze_stack.push(p);
|
331
|
-
int top = analyze_toclear.size();
|
332
|
-
while (analyze_stack.size() > 0){
|
333
|
-
assert(reason[var(analyze_stack.last())] != GClause_NULL);
|
334
|
-
GClause r = reason[var(analyze_stack.last())]; analyze_stack.pop();
|
335
|
-
Clause& c = r.isLit() ? ((*analyze_tmpbin)[1] = r.lit(), *analyze_tmpbin)
|
336
|
-
: *r.clause();
|
337
|
-
for (int i = 1; i < c.size(); i++){
|
338
|
-
Lit p = c[i];
|
339
|
-
if (!analyze_seen[var(p)] && level[var(p)] != 0){
|
340
|
-
if (reason[var(p)] != GClause_NULL && ((1 << (level[var(p)] & 31)) & min_level) != 0){
|
341
|
-
analyze_seen[var(p)] = 1;
|
342
|
-
analyze_stack.push(p);
|
343
|
-
analyze_toclear.push(p);
|
344
|
-
}else{
|
345
|
-
for (int j = top; j < analyze_toclear.size(); j++)
|
346
|
-
analyze_seen[var(analyze_toclear[j])] = 0;
|
347
|
-
analyze_toclear.shrink(analyze_toclear.size() - top);
|
348
|
-
return false;
|
349
|
-
}
|
350
|
-
}
|
351
|
-
}
|
352
|
-
}
|
353
|
-
|
354
|
-
return true;
|
355
|
-
}
|
356
|
-
|
357
|
-
|
358
|
-
/*_________________________________________________________________________________________________
|
359
|
-
|
|
360
|
-
| analyzeFinal : (confl : Clause*) (skip_first : bool) -> [void]
|
361
|
-
|
|
362
|
-
| Description:
|
363
|
-
| Specialized analysis procedure to express the final conflict in terms of assumptions.
|
364
|
-
| 'root_level' is allowed to point beyond end of trace (useful if called after conflict while
|
365
|
-
| making assumptions). If 'skip_first' is TRUE, the first literal of 'confl' is ignored (needed
|
366
|
-
| if conflict arose before search even started).
|
367
|
-
|________________________________________________________________________________________________@*/
|
368
|
-
void Solver::analyzeFinal(Clause* confl, bool skip_first)
|
369
|
-
{
|
370
|
-
// -- NOTE! This code is relatively untested. Please report bugs!
|
371
|
-
conflict.clear();
|
372
|
-
if (root_level == 0) return;
|
373
|
-
|
374
|
-
vec<char>& seen = analyze_seen;
|
375
|
-
for (int i = skip_first ? 1 : 0; i < confl->size(); i++){
|
376
|
-
Var x = var((*confl)[i]);
|
377
|
-
if (level[x] > 0)
|
378
|
-
seen[x] = 1;
|
379
|
-
}
|
380
|
-
|
381
|
-
int start = (root_level >= trail_lim.size()) ? trail.size()-1 : trail_lim[root_level];
|
382
|
-
for (int i = start; i >= trail_lim[0]; i--){
|
383
|
-
Var x = var(trail[i]);
|
384
|
-
if (seen[x]){
|
385
|
-
GClause r = reason[x];
|
386
|
-
if (r == GClause_NULL){
|
387
|
-
assert(level[x] > 0);
|
388
|
-
conflict.push(~trail[i]);
|
389
|
-
}else{
|
390
|
-
if (r.isLit()){
|
391
|
-
Lit p = r.lit();
|
392
|
-
if (level[var(p)] > 0)
|
393
|
-
seen[var(p)] = 1;
|
394
|
-
}else{
|
395
|
-
Clause& c = *r.clause();
|
396
|
-
for (int j = 1; j < c.size(); j++)
|
397
|
-
if (level[var(c[j])] > 0)
|
398
|
-
seen[var(c[j])] = 1;
|
399
|
-
}
|
400
|
-
}
|
401
|
-
seen[x] = 0;
|
402
|
-
}
|
403
|
-
}
|
404
|
-
}
|
405
|
-
|
406
|
-
|
407
|
-
/*_________________________________________________________________________________________________
|
408
|
-
|
|
409
|
-
| enqueue : (p : Lit) (from : Clause*) -> [bool]
|
410
|
-
|
|
411
|
-
| Description:
|
412
|
-
| Puts a new fact on the propagation queue as well as immediately updating the variable's value.
|
413
|
-
| Should a conflict arise, FALSE is returned.
|
414
|
-
|
|
415
|
-
| Input:
|
416
|
-
| p - The fact to enqueue
|
417
|
-
| from - [Optional] Fact propagated from this (currently) unit clause. Stored in 'reason[]'.
|
418
|
-
| Default value is NULL (no reason).
|
419
|
-
|
|
420
|
-
| Output:
|
421
|
-
| TRUE if fact was enqueued without conflict, FALSE otherwise.
|
422
|
-
|________________________________________________________________________________________________@*/
|
423
|
-
bool Solver::enqueue(Lit p, GClause from)
|
424
|
-
{
|
425
|
-
if (value(p) != l_Undef)
|
426
|
-
return value(p) != l_False;
|
427
|
-
else{
|
428
|
-
assigns[var(p)] = toInt(lbool(!sign(p)));
|
429
|
-
level [var(p)] = decisionLevel();
|
430
|
-
reason [var(p)] = from;
|
431
|
-
trail.push(p);
|
432
|
-
return true;
|
433
|
-
}
|
434
|
-
}
|
435
|
-
|
436
|
-
|
437
|
-
/*_________________________________________________________________________________________________
|
438
|
-
|
|
439
|
-
| propagate : [void] -> [Clause*]
|
440
|
-
|
|
441
|
-
| Description:
|
442
|
-
| Propagates all enqueued facts. If a conflict arises, the conflicting clause is returned,
|
443
|
-
| otherwise NULL.
|
444
|
-
|
|
445
|
-
| Post-conditions:
|
446
|
-
| * the propagation queue is empty, even if there was a conflict.
|
447
|
-
|________________________________________________________________________________________________@*/
|
448
|
-
Clause* Solver::propagate()
|
449
|
-
{
|
450
|
-
Clause* confl = NULL;
|
451
|
-
while (qhead < trail.size()){
|
452
|
-
stats.propagations++;
|
453
|
-
simpDB_props--;
|
454
|
-
|
455
|
-
Lit p = trail[qhead++]; // 'p' is enqueued fact to propagate.
|
456
|
-
vec<GClause>& ws = watches[index(p)];
|
457
|
-
GClause* i,* j, *end;
|
458
|
-
|
459
|
-
for (i = j = (GClause*)ws, end = i + ws.size(); i != end;){
|
460
|
-
if (i->isLit()){
|
461
|
-
if (!enqueue(i->lit(), GClause_new(p))){
|
462
|
-
if (decisionLevel() == 0)
|
463
|
-
ok = false;
|
464
|
-
confl = propagate_tmpbin;
|
465
|
-
(*confl)[1] = ~p;
|
466
|
-
(*confl)[0] = i->lit();
|
467
|
-
|
468
|
-
qhead = trail.size();
|
469
|
-
// Copy the remaining watches:
|
470
|
-
while (i < end)
|
471
|
-
*j++ = *i++;
|
472
|
-
}else
|
473
|
-
*j++ = *i++;
|
474
|
-
}else{
|
475
|
-
Clause& c = *i->clause(); i++;
|
476
|
-
assert(c.size() > 2);
|
477
|
-
// Make sure the false literal is data[1]:
|
478
|
-
Lit false_lit = ~p;
|
479
|
-
if (c[0] == false_lit)
|
480
|
-
c[0] = c[1], c[1] = false_lit;
|
481
|
-
|
482
|
-
assert(c[1] == false_lit);
|
483
|
-
|
484
|
-
// If 0th watch is true, then clause is already satisfied.
|
485
|
-
Lit first = c[0];
|
486
|
-
lbool val = value(first);
|
487
|
-
if (val == l_True){
|
488
|
-
*j++ = GClause_new(&c);
|
489
|
-
}else{
|
490
|
-
// Look for new watch:
|
491
|
-
for (int k = 2; k < c.size(); k++)
|
492
|
-
if (value(c[k]) != l_False){
|
493
|
-
c[1] = c[k]; c[k] = false_lit;
|
494
|
-
watches[index(~c[1])].push(GClause_new(&c));
|
495
|
-
goto FoundWatch; }
|
496
|
-
|
497
|
-
// Did not find watch -- clause is unit under assignment:
|
498
|
-
*j++ = GClause_new(&c);
|
499
|
-
if (!enqueue(first, GClause_new(&c))){
|
500
|
-
if (decisionLevel() == 0)
|
501
|
-
ok = false;
|
502
|
-
confl = &c;
|
503
|
-
qhead = trail.size();
|
504
|
-
// Copy the remaining watches:
|
505
|
-
while (i < end)
|
506
|
-
*j++ = *i++;
|
507
|
-
}
|
508
|
-
FoundWatch:;
|
509
|
-
}
|
510
|
-
}
|
511
|
-
}
|
512
|
-
ws.shrink(i - j);
|
513
|
-
}
|
514
|
-
|
515
|
-
return confl;
|
516
|
-
}
|
517
|
-
|
518
|
-
|
519
|
-
/*_________________________________________________________________________________________________
|
520
|
-
|
|
521
|
-
| reduceDB : () -> [void]
|
522
|
-
|
|
523
|
-
| Description:
|
524
|
-
| Remove half of the learnt clauses, minus the clauses locked by the current assignment. Locked
|
525
|
-
| clauses are clauses that are reason to some assignment. Binary clauses are never removed.
|
526
|
-
|________________________________________________________________________________________________@*/
|
527
|
-
struct reduceDB_lt { bool operator () (Clause* x, Clause* y) { return x->size() > 2 && (y->size() == 2 || x->activity() < y->activity()); } };
|
528
|
-
void Solver::reduceDB()
|
529
|
-
{
|
530
|
-
int i, j;
|
531
|
-
double extra_lim = cla_inc / learnts.size(); // Remove any clause below this activity
|
532
|
-
|
533
|
-
sort(learnts, reduceDB_lt());
|
534
|
-
for (i = j = 0; i < learnts.size() / 2; i++){
|
535
|
-
if (learnts[i]->size() > 2 && !locked(learnts[i]))
|
536
|
-
remove(learnts[i]);
|
537
|
-
else
|
538
|
-
learnts[j++] = learnts[i];
|
539
|
-
}
|
540
|
-
for (; i < learnts.size(); i++){
|
541
|
-
if (learnts[i]->size() > 2 && !locked(learnts[i]) && learnts[i]->activity() < extra_lim)
|
542
|
-
remove(learnts[i]);
|
543
|
-
else
|
544
|
-
learnts[j++] = learnts[i];
|
545
|
-
}
|
546
|
-
learnts.shrink(i - j);
|
547
|
-
}
|
548
|
-
|
549
|
-
|
550
|
-
/*_________________________________________________________________________________________________
|
551
|
-
|
|
552
|
-
| simplifyDB : [void] -> [bool]
|
553
|
-
|
|
554
|
-
| Description:
|
555
|
-
| Simplify the clause database according to the current top-level assigment. Currently, the only
|
556
|
-
| thing done here is the removal of satisfied clauses, but more things can be put here.
|
557
|
-
|________________________________________________________________________________________________@*/
|
558
|
-
void Solver::simplifyDB()
|
559
|
-
{
|
560
|
-
if (!ok) return; // GUARD (public method)
|
561
|
-
assert(decisionLevel() == 0);
|
562
|
-
|
563
|
-
if (propagate() != NULL){
|
564
|
-
ok = false;
|
565
|
-
return; }
|
566
|
-
|
567
|
-
if (nAssigns() == simpDB_assigns || simpDB_props > 0) // (nothing has changed or preformed a simplification too recently)
|
568
|
-
return;
|
569
|
-
|
570
|
-
// Clear watcher lists:
|
571
|
-
for (int i = simpDB_assigns; i < nAssigns(); i++){
|
572
|
-
Lit p = trail[i];
|
573
|
-
vec<GClause>& ws = watches[index(~p)];
|
574
|
-
for (int j = 0; j < ws.size(); j++)
|
575
|
-
if (ws[j].isLit())
|
576
|
-
if (removeWatch(watches[index(~ws[j].lit())], GClause_new(p))) // (remove binary GClause from "other" watcher list)
|
577
|
-
n_bin_clauses--;
|
578
|
-
watches[index( p)].clear(true);
|
579
|
-
watches[index(~p)].clear(true);
|
580
|
-
}
|
581
|
-
|
582
|
-
// Remove satisfied clauses:
|
583
|
-
for (int type = 0; type < 2; type++){
|
584
|
-
vec<Clause*>& cs = type ? learnts : clauses;
|
585
|
-
int j = 0;
|
586
|
-
for (int i = 0; i < cs.size(); i++){
|
587
|
-
if (!locked(cs[i]) && simplify(cs[i])) // (the test for 'locked()' is currently superfluous, but without it the reason-graph is not correctly maintained for decision level 0)
|
588
|
-
remove(cs[i]);
|
589
|
-
else
|
590
|
-
cs[j++] = cs[i];
|
591
|
-
}
|
592
|
-
cs.shrink(cs.size()-j);
|
593
|
-
}
|
594
|
-
|
595
|
-
simpDB_assigns = nAssigns();
|
596
|
-
simpDB_props = stats.clauses_literals + stats.learnts_literals; // (shouldn't depend on 'stats' really, but it will do for now)
|
597
|
-
}
|
598
|
-
|
599
|
-
|
600
|
-
/*_________________________________________________________________________________________________
|
601
|
-
|
|
602
|
-
| search : (nof_conflicts : int) (nof_learnts : int) (params : const SearchParams&) -> [lbool]
|
603
|
-
|
|
604
|
-
| Description:
|
605
|
-
| Search for a model the specified number of conflicts, keeping the number of learnt clauses
|
606
|
-
| below the provided limit. NOTE! Use negative value for 'nof_conflicts' or 'nof_learnts' to
|
607
|
-
| indicate infinity.
|
608
|
-
|
|
609
|
-
| Output:
|
610
|
-
| 'l_True' if a partial assigment that is consistent with respect to the clauseset is found. If
|
611
|
-
| all variables are decision variables, this means that the clause set is satisfiable. 'l_False'
|
612
|
-
| if the clause set is unsatisfiable. 'l_Undef' if the bound on number of conflicts is reached.
|
613
|
-
|________________________________________________________________________________________________@*/
|
614
|
-
lbool Solver::search(int nof_conflicts, int nof_learnts, const SearchParams& params)
|
615
|
-
{
|
616
|
-
if (!ok) return l_False; // GUARD (public method)
|
617
|
-
assert(root_level == decisionLevel());
|
618
|
-
|
619
|
-
stats.starts++;
|
620
|
-
int conflictC = 0;
|
621
|
-
var_decay = 1 / params.var_decay;
|
622
|
-
cla_decay = 1 / params.clause_decay;
|
623
|
-
model.clear();
|
624
|
-
|
625
|
-
for (;;){
|
626
|
-
Clause* confl = propagate();
|
627
|
-
if (confl != NULL){
|
628
|
-
// CONFLICT
|
629
|
-
|
630
|
-
stats.conflicts++; conflictC++;
|
631
|
-
vec<Lit> learnt_clause;
|
632
|
-
int backtrack_level;
|
633
|
-
if (decisionLevel() == root_level){
|
634
|
-
// Contradiction found:
|
635
|
-
analyzeFinal(confl);
|
636
|
-
return l_False; }
|
637
|
-
analyze(confl, learnt_clause, backtrack_level);
|
638
|
-
cancelUntil(max(backtrack_level, root_level));
|
639
|
-
newClause(learnt_clause, true);
|
640
|
-
if (learnt_clause.size() == 1) level[var(learnt_clause[0])] = 0; // (this is ugly (but needed for 'analyzeFinal()') -- in future versions, we will backtrack past the 'root_level' and redo the assumptions)
|
641
|
-
varDecayActivity();
|
642
|
-
claDecayActivity();
|
643
|
-
|
644
|
-
}else{
|
645
|
-
// NO CONFLICT
|
646
|
-
|
647
|
-
if (nof_conflicts >= 0 && conflictC >= nof_conflicts){
|
648
|
-
// Reached bound on number of conflicts:
|
649
|
-
progress_estimate = progressEstimate();
|
650
|
-
cancelUntil(root_level);
|
651
|
-
return l_Undef; }
|
652
|
-
|
653
|
-
if (decisionLevel() == 0)
|
654
|
-
// Simplify the set of problem clauses:
|
655
|
-
simplifyDB(), assert(ok);
|
656
|
-
|
657
|
-
if (nof_learnts >= 0 && learnts.size()-nAssigns() >= nof_learnts)
|
658
|
-
// Reduce the set of learnt clauses:
|
659
|
-
reduceDB();
|
660
|
-
|
661
|
-
// New variable decision:
|
662
|
-
stats.decisions++;
|
663
|
-
Var next = order.select(params.random_var_freq);
|
664
|
-
|
665
|
-
if (next == var_Undef){
|
666
|
-
// Model found:
|
667
|
-
model.growTo(nVars());
|
668
|
-
for (int i = 0; i < nVars(); i++) model[i] = value(i);
|
669
|
-
cancelUntil(root_level);
|
670
|
-
return l_True;
|
671
|
-
}
|
672
|
-
|
673
|
-
check(assume(~Lit(next)));
|
674
|
-
}
|
675
|
-
}
|
676
|
-
}
|
677
|
-
|
678
|
-
|
679
|
-
// Return search-space coverage. Not extremely reliable.
|
680
|
-
//
|
681
|
-
double Solver::progressEstimate()
|
682
|
-
{
|
683
|
-
double progress = 0;
|
684
|
-
double F = 1.0 / nVars();
|
685
|
-
for (int i = 0; i < nVars(); i++)
|
686
|
-
if (value(i) != l_Undef)
|
687
|
-
progress += pow(F, level[i]);
|
688
|
-
return progress / nVars();
|
689
|
-
}
|
690
|
-
|
691
|
-
|
692
|
-
// Divide all variable activities by 1e100.
|
693
|
-
//
|
694
|
-
void Solver::varRescaleActivity()
|
695
|
-
{
|
696
|
-
for (int i = 0; i < nVars(); i++)
|
697
|
-
activity[i] *= 1e-100;
|
698
|
-
var_inc *= 1e-100;
|
699
|
-
}
|
700
|
-
|
701
|
-
|
702
|
-
// Divide all constraint activities by 1e100.
|
703
|
-
//
|
704
|
-
void Solver::claRescaleActivity()
|
705
|
-
{
|
706
|
-
for (int i = 0; i < learnts.size(); i++)
|
707
|
-
learnts[i]->activity() *= 1e-20;
|
708
|
-
cla_inc *= 1e-20;
|
709
|
-
}
|
710
|
-
|
711
|
-
|
712
|
-
/*_________________________________________________________________________________________________
|
713
|
-
|
|
714
|
-
| solve : (assumps : const vec<Lit>&) -> [bool]
|
715
|
-
|
|
716
|
-
| Description:
|
717
|
-
| Top-level solve. If using assumptions (non-empty 'assumps' vector), you must call
|
718
|
-
| 'simplifyDB()' first to see that no top-level conflict is present (which would put the solver
|
719
|
-
| in an undefined state).
|
720
|
-
|________________________________________________________________________________________________@*/
|
721
|
-
bool Solver::solve(const vec<Lit>& assumps)
|
722
|
-
{
|
723
|
-
simplifyDB();
|
724
|
-
if (!ok) return false;
|
725
|
-
|
726
|
-
SearchParams params(default_params);
|
727
|
-
double nof_conflicts = 100;
|
728
|
-
double nof_learnts = nClauses() / 3;
|
729
|
-
lbool status = l_Undef;
|
730
|
-
|
731
|
-
// Perform assumptions:
|
732
|
-
root_level = assumps.size();
|
733
|
-
for (int i = 0; i < assumps.size(); i++){
|
734
|
-
Lit p = assumps[i];
|
735
|
-
assert(var(p) < nVars());
|
736
|
-
if (!assume(p)){
|
737
|
-
GClause r = reason[var(p)];
|
738
|
-
if (r != GClause_NULL){
|
739
|
-
Clause* confl;
|
740
|
-
if (r.isLit()){
|
741
|
-
confl = propagate_tmpbin;
|
742
|
-
(*confl)[1] = ~p;
|
743
|
-
(*confl)[0] = r.lit();
|
744
|
-
}else
|
745
|
-
confl = r.clause();
|
746
|
-
analyzeFinal(confl, true);
|
747
|
-
conflict.push(~p);
|
748
|
-
}else
|
749
|
-
conflict.clear(),
|
750
|
-
conflict.push(~p);
|
751
|
-
cancelUntil(0);
|
752
|
-
return false; }
|
753
|
-
Clause* confl = propagate();
|
754
|
-
if (confl != NULL){
|
755
|
-
analyzeFinal(confl), assert(conflict.size() > 0);
|
756
|
-
cancelUntil(0);
|
757
|
-
return false; }
|
758
|
-
}
|
759
|
-
assert(root_level == decisionLevel());
|
760
|
-
|
761
|
-
// Search:
|
762
|
-
if (verbosity >= 1){
|
763
|
-
reportf("==================================[MINISAT]===================================\n");
|
764
|
-
reportf("| Conflicts | ORIGINAL | LEARNT | Progress |\n");
|
765
|
-
reportf("| | Clauses Literals | Limit Clauses Literals Lit/Cl | |\n");
|
766
|
-
reportf("==============================================================================\n");
|
767
|
-
}
|
768
|
-
|
769
|
-
while (status == l_Undef){
|
770
|
-
if (verbosity >= 1)
|
771
|
-
reportf("| %9d | %7d %8d | %7d %7d %8d %7.1f | %6.3f %% |\n", (int)stats.conflicts, nClauses(), (int)stats.clauses_literals, (int)nof_learnts, nLearnts(), (int)stats.learnts_literals, (double)stats.learnts_literals/nLearnts(), progress_estimate*100);
|
772
|
-
status = search((int)nof_conflicts, (int)nof_learnts, params);
|
773
|
-
nof_conflicts *= 1.5;
|
774
|
-
nof_learnts *= 1.1;
|
775
|
-
}
|
776
|
-
if (verbosity >= 1)
|
777
|
-
reportf("==============================================================================\n");
|
778
|
-
|
779
|
-
cancelUntil(0);
|
780
|
-
return status == l_True;
|
781
|
-
}
|