1 /******************************************
2 Copyright (c) 2016, Mate Soos
3 
4 Permission is hereby granted, free of charge, to any person obtaining a copy
5 of this software and associated documentation files (the "Software"), to deal
6 in the Software without restriction, including without limitation the rights
7 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 copies of the Software, and to permit persons to whom the Software is
9 furnished to do so, subject to the following conditions:
10 
11 The above copyright notice and this permission notice shall be included in
12 all copies or substantial portions of the Software.
13 
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 THE SOFTWARE.
21 ***********************************************/
22 
23 #include "xorfinder.h"
24 #include "time_mem.h"
25 #include "solver.h"
26 #include "occsimplifier.h"
27 #include "clauseallocator.h"
28 #include "sqlstats.h"
29 #include "varreplacer.h"
30 
31 #include <limits>
32 //#define XOR_DEBUG
33 
34 using namespace CMSat;
35 using std::cout;
36 using std::endl;
37 
XorFinder(OccSimplifier * _occsimplifier,Solver * _solver)38 XorFinder::XorFinder(OccSimplifier* _occsimplifier, Solver* _solver) :
39     xors(_solver->xorclauses)
40     , unused_xors(_solver->xorclauses_unused)
41     , occsimplifier(_occsimplifier)
42     , solver(_solver)
43     , toClear(_solver->toClear)
44     , seen(_solver->seen)
45     , seen2(_solver->seen2)
46 {
47     tmp_vars_xor_two.reserve(2000);
48 }
49 
find_xors_based_on_long_clauses()50 void XorFinder::find_xors_based_on_long_clauses()
51 {
52     #ifdef DEBUG_MARKED_CLAUSE
53     assert(solver->no_marked_clauses());
54     #endif
55 
56     vector<Lit> lits;
57     for (vector<ClOffset>::iterator
58         it = occsimplifier->clauses.begin()
59         , end = occsimplifier->clauses.end()
60         ; it != end && xor_find_time_limit > 0
61         ; ++it
62     ) {
63         ClOffset offset = *it;
64         Clause* cl = solver->cl_alloc.ptr(offset);
65         xor_find_time_limit -= 1;
66 
67         //Already freed
68         if (cl->freed() || cl->getRemoved() || cl->red()) {
69             continue;
70         }
71 
72         //Too large -> too expensive
73         if (cl->size() > solver->conf.maxXorToFind) {
74             continue;
75         }
76 
77         //If not tried already, find an XOR with it
78         if (!cl->stats.marked_clause ) {
79             cl->stats.marked_clause = true;
80             assert(!cl->getRemoved());
81 
82             size_t needed_per_ws = 1ULL << (cl->size()-2);
83             //let's allow shortened clauses
84             needed_per_ws >>= 1;
85 
86             for(const Lit lit: *cl) {
87                 if (solver->watches[lit].size() < needed_per_ws) {
88                     goto next;
89                 }
90                 if (solver->watches[~lit].size() < needed_per_ws) {
91                     goto next;
92                 }
93             }
94 
95             lits.resize(cl->size());
96             std::copy(cl->begin(), cl->end(), lits.begin());
97             findXor(lits, offset, cl->abst);
98             next:;
99         }
100     }
101 }
102 
clean_equivalent_xors(vector<Xor> & txors)103 void XorFinder::clean_equivalent_xors(vector<Xor>& txors)
104 {
105     if (!txors.empty()) {
106         size_t orig_size = txors.size();
107         for(Xor& x: txors) {
108             std::sort(x.begin(), x.end());
109         }
110         std::sort(txors.begin(), txors.end());
111 
112         vector<Xor>::iterator i = txors.begin();
113         vector<Xor>::iterator j = i;
114         i++;
115         uint64_t size = 1;
116         for(vector<Xor>::iterator end = txors.end(); i != end; i++) {
117             if (j->vars == i->vars && i->rhs == j->rhs) {
118                 j->merge_clash(*i, seen);
119                 j->detached |= i->detached;
120             } else {
121                 j++;
122                 *j = *i;
123                 size++;
124             }
125         }
126         txors.resize(size);
127 
128         if (solver->conf.verbosity) {
129             cout << "c [xor-clean-equiv] removed equivalent xors: "
130             << (orig_size-txors.size()) << " left with: " << txors.size()
131             << endl;
132         }
133     }
134 }
135 
find_xors()136 void XorFinder::find_xors()
137 {
138     runStats.clear();
139     runStats.numCalls = 1;
140     grab_mem();
141     if ((solver->conf.xor_var_per_cut + 2) > solver->conf.maxXorToFind) {
142         if (solver->conf.verbosity) {
143             cout << "c WARNING updating max XOR to find to "
144             << (solver->conf.xor_var_per_cut + 2)
145             << " as the current number was lower than the cutting number" << endl;
146         }
147         solver->conf.maxXorToFind = solver->conf.xor_var_per_cut + 2;
148     }
149 
150     //Clear flags. This is super-important.
151     for(auto& offs: occsimplifier->clauses) {
152         Clause* cl = solver->cl_alloc.ptr(offs);
153         if (cl->getRemoved() || cl->freed()) {
154             continue;
155         }
156 
157         cl->set_used_in_xor(false);
158         cl->set_used_in_xor_full(false);
159     }
160     xors.clear();
161     unused_xors.clear();
162 
163     double myTime = cpuTime();
164     const int64_t orig_xor_find_time_limit =
165         1000LL*1000LL*solver->conf.xor_finder_time_limitM
166         *solver->conf.global_timeout_multiplier;
167 
168     xor_find_time_limit = orig_xor_find_time_limit;
169 
170     occsimplifier->sort_occurs_and_set_abst();
171     if (solver->conf.verbosity) {
172         cout << "c [occ-xor] sort occur list T: " << (cpuTime()-myTime) << endl;
173     }
174 
175     #ifdef DEBUG_MARKED_CLAUSE
176     assert(solver->no_marked_clauses());
177     #endif
178 
179     find_xors_based_on_long_clauses();
180     assert(runStats.foundXors == xors.size());
181 
182     //clean them of equivalent XORs
183     clean_equivalent_xors(xors);
184 
185     //Cleanup
186     for(ClOffset offset: occsimplifier->clauses) {
187         Clause* cl = solver->cl_alloc.ptr(offset);
188         cl->stats.marked_clause = false;
189     }
190 
191     //Print stats
192     const bool time_out = (xor_find_time_limit < 0);
193     const double time_remain = float_div(xor_find_time_limit, orig_xor_find_time_limit);
194     runStats.findTime = cpuTime() - myTime;
195     runStats.time_outs += time_out;
196     solver->sumSearchStats.num_xors_found_last = xors.size();
197     print_found_xors();
198 
199     if (solver->conf.verbosity) {
200         runStats.print_short(solver, time_remain);
201     }
202     globalStats += runStats;
203 
204     if (solver->sqlStats) {
205         solver->sqlStats->time_passed(
206             solver
207             , "xor-find"
208             , cpuTime() - myTime
209             , time_out
210             , time_remain
211         );
212     }
213     solver->xor_clauses_updated = true;
214 
215     #if defined(SLOW_DEBUG) || defined(XOR_DEBUG)
216     for(const Xor& x: xors) {
217         for(uint32_t v: x) {
218             assert(solver->varData[v].removed == Removed::none);
219         }
220     }
221     #endif
222 }
223 
print_found_xors()224 void XorFinder::print_found_xors()
225 {
226     if (solver->conf.verbosity >= 5) {
227         cout << "c Found XORs: " << endl;
228         for(vector<Xor>::const_iterator
229             it = xors.begin(), end = xors.end()
230             ; it != end
231             ; ++it
232         ) {
233             cout << "c " << *it << endl;
234         }
235         cout << "c -> Total: " << xors.size() << " xors" << endl;
236     }
237 }
238 
findXor(vector<Lit> & lits,const ClOffset offset,cl_abst_type abst)239 void XorFinder::findXor(vector<Lit>& lits, const ClOffset offset, cl_abst_type abst)
240 {
241     //Set this clause as the base for the XOR, fill 'seen'
242     xor_find_time_limit -= lits.size()/4+1;
243     poss_xor.setup(lits, offset, abst, occcnt);
244 
245     //Run findXorMatch for the 2 smallest watchlists
246     Lit slit = lit_Undef;
247     Lit slit2 = lit_Undef;
248     uint32_t smallest = std::numeric_limits<uint32_t>::max();
249     uint32_t smallest2 = std::numeric_limits<uint32_t>::max();
250     for (size_t i = 0, end = lits.size(); i < end; i++) {
251         const Lit lit = lits[i];
252         uint32_t num = solver->watches[lit].size();
253         num += solver->watches[~lit].size();
254         if (num < smallest) {
255             slit2 = slit;
256             smallest2 = smallest;
257 
258             slit = lit;
259             smallest = num;
260         } else if (num < smallest2) {
261             slit2 = lit;
262             smallest2 = num;
263         }
264     }
265     findXorMatch(solver->watches[slit], slit);
266     findXorMatch(solver->watches[~slit], ~slit);
267 
268     if (lits.size() <= solver->conf.maxXorToFindSlow) {
269         findXorMatch(solver->watches[slit2], slit2);
270         findXorMatch(solver->watches[~slit2], ~slit2);
271     }
272 
273     if (poss_xor.foundAll()) {
274         std::sort(lits.begin(), lits.end());
275         Xor found_xor(lits, poss_xor.getRHS(), vector<uint32_t>());
276         #if defined(SLOW_DEBUG) || defined(XOR_DEBUG)
277         for(Lit lit: lits) {
278             assert(solver->varData[lit.var()].removed == Removed::none);
279         }
280         #endif
281 
282         add_found_xor(found_xor);
283         assert(poss_xor.get_fully_used().size() == poss_xor.get_offsets().size());
284         for(uint32_t i = 0; i < poss_xor.get_offsets().size() ; i++) {
285             ClOffset offs = poss_xor.get_offsets()[i];
286             bool fully_used = poss_xor.get_fully_used()[i];
287 
288             Clause* cl = solver->cl_alloc.ptr(offs);
289             assert(!cl->getRemoved());
290             cl->set_used_in_xor(true);
291             cl->set_used_in_xor_full(fully_used);
292         }
293     }
294     poss_xor.clear_seen(occcnt);
295 }
296 
add_found_xor(const Xor & found_xor)297 void XorFinder::add_found_xor(const Xor& found_xor)
298 {
299     xors.push_back(found_xor);
300     runStats.foundXors++;
301     runStats.sumSizeXors += found_xor.size();
302     runStats.maxsize = std::max<uint32_t>(runStats.maxsize, found_xor.size());
303     runStats.minsize = std::min<uint32_t>(runStats.minsize, found_xor.size());
304 }
305 
findXorMatch(watch_subarray_const occ,const Lit wlit)306 void XorFinder::findXorMatch(watch_subarray_const occ, const Lit wlit)
307 {
308     xor_find_time_limit -= (int64_t)occ.size()/8+1;
309     for (const Watched& w: occ) {
310         if (w.isIdx()) {
311             continue;
312         }
313         assert(poss_xor.getSize() > 2);
314 
315         if (w.isBin()) {
316             #ifdef SLOW_DEBUG
317             assert(occcnt[wlit.var()]);
318             #endif
319 
320             if (w.red()) {
321                 continue;
322             }
323 
324             if (!occcnt[w.lit2().var()]) {
325                 goto end;
326             }
327 
328             binvec.clear();
329             binvec.resize(2);
330             binvec[0] = w.lit2();
331             binvec[1] = wlit;
332             if (binvec[0] > binvec[1]) {
333                 std::swap(binvec[0], binvec[1]);
334             }
335 
336             xor_find_time_limit -= 1;
337             poss_xor.add(binvec, std::numeric_limits<ClOffset>::max(), varsMissing);
338             if (poss_xor.foundAll())
339                 break;
340         } else {
341             if (w.getBlockedLit().toInt() == lit_Undef.toInt())
342                 //Clauses are ordered, lit_Undef means it's larger than maxXorToFind
343                 break;
344 
345             if (w.getBlockedLit().toInt() == lit_Error.toInt())
346                 //lit_Error means it's freed or removed, and it's ordered so no more
347                 break;
348 
349             if ((w.getBlockedLit().toInt() | poss_xor.getAbst()) != poss_xor.getAbst())
350                 continue;
351 
352             xor_find_time_limit -= 3;
353             const ClOffset offset = w.get_offset();
354             Clause& cl = *solver->cl_alloc.ptr(offset);
355             if (cl.freed() || cl.getRemoved() || cl.red()) {
356                 //Clauses are ordered!!
357                 break;
358             }
359 
360             //Allow the clause to be smaller or equal in size
361             if (cl.size() > poss_xor.getSize()) {
362                 //clauses are ordered!!
363                 break;
364             }
365 
366             //For longer clauses, don't the the fancy algo that can
367             //deal with incomplete XORs
368             if (cl.size() != poss_xor.getSize()
369                 && poss_xor.getSize() > solver->conf.maxXorToFindSlow
370             ) {
371                 break;
372             }
373 
374             //Doesn't contain variables not in the original clause
375             #if defined(SLOW_DEBUG) || defined(XOR_DEBUG)
376             assert(cl.abst == calcAbstraction(cl));
377             #endif
378             if ((cl.abst | poss_xor.getAbst()) != poss_xor.getAbst())
379                 continue;
380 
381             //Check RHS, vars inside
382             bool rhs = true;
383             for (const Lit cl_lit :cl) {
384                 //early-abort, contains literals not in original clause
385                 if (!occcnt[cl_lit.var()])
386                     goto end;
387 
388                 rhs ^= cl_lit.sign();
389             }
390             //either the invertedness has to match, or the size must be smaller
391             if (rhs != poss_xor.getRHS() && cl.size() == poss_xor.getSize())
392                 continue;
393 
394             //If the size of this clause is the same of the base clause, then
395             //there is no point in using this clause as a base for another XOR
396             //because exactly the same things will be found.
397             if (cl.size() == poss_xor.getSize()) {
398                 cl.stats.marked_clause = true;
399             }
400 
401             xor_find_time_limit -= cl.size()/4+1;
402             poss_xor.add(cl, offset, varsMissing);
403             if (poss_xor.foundAll())
404                 break;
405         }
406         end:;
407     }
408 }
409 
remove_xors_without_connecting_vars(const vector<Xor> & this_xors)410 vector<Xor> XorFinder::remove_xors_without_connecting_vars(const vector<Xor>& this_xors)
411 {
412     if (this_xors.empty())
413         return this_xors;
414 
415     double myTime = cpuTime();
416     vector<Xor> ret;
417     assert(toClear.empty());
418 
419     //Fill "seen" with vars used
420     uint32_t non_empty = 0;
421     for(const Xor& x: this_xors) {
422         if (x.size() != 0) {
423             non_empty++;
424         }
425 
426         for(uint32_t v: x) {
427             if (solver->seen[v] == 0) {
428                 toClear.push_back(Lit(v, false));
429             }
430 
431             if (solver->seen[v] < 2) {
432                 solver->seen[v]++;
433             }
434         }
435     }
436 
437     //has at least 1 var with occur of 2
438     for(const Xor& x: this_xors) {
439         if (xor_has_interesting_var(x) || x.detached) {
440             #ifdef VERBOSE_DEBUG
441             cout << "XOR has connecting var: " << x << endl;
442             #endif
443             ret.push_back(x);
444         } else {
445             #ifdef VERBOSE_DEBUG
446             cout << "XOR has no connecting var: " << x << endl;
447             #endif
448             unused_xors.push_back(x);
449         }
450     }
451 
452     //clear "seen"
453     for(Lit l: toClear) {
454         solver->seen[l.var()] = 0;
455     }
456     toClear.clear();
457 
458     double time_used = cpuTime() - myTime;
459     if (solver->conf.verbosity) {
460         cout << "c [xor-rem-unconnected] left with " <<  ret.size()
461         << " xors from " << non_empty << " non-empty xors"
462         << solver->conf.print_times(time_used)
463         << endl;
464     }
465     if (solver->sqlStats) {
466         solver->sqlStats->time_passed_min(
467             solver
468             , "xor-rem-no-connecting-vars"
469             , time_used
470         );
471     }
472 
473     return ret;
474 }
475 
xor_together_xors(vector<Xor> & this_xors)476 bool XorFinder::xor_together_xors(vector<Xor>& this_xors)
477 {
478     if (occcnt.size() != solver->nVars())
479         grab_mem();
480 
481     if (this_xors.empty())
482         return solver->okay();
483 
484     #ifdef SLOW_DEBUG
485     for(auto x: occcnt) {
486         assert(x == 0);
487     }
488     #endif
489 
490     if (solver->conf.verbosity >= 5) {
491         cout << "c XOR-ing together XORs. Starting with: " << endl;
492         for(const auto& x: this_xors) {
493             cout << "c XOR before xor-ing together: " << x << endl;
494         }
495     }
496 
497     assert(solver->okay());
498     assert(solver->decisionLevel() == 0);
499     assert(solver->watches.get_smudged_list().empty());
500     const size_t origsize = this_xors.size();
501 
502     uint32_t xored = 0;
503     const double myTime = cpuTime();
504     assert(toClear.empty());
505 
506     //Link in xors into watchlist
507     for(size_t i = 0; i < this_xors.size(); i++) {
508         const Xor& x = this_xors[i];
509         for(uint32_t v: x) {
510             if (occcnt[v] == 0) {
511                 toClear.push_back(Lit(v, false));
512             }
513             occcnt[v]++;
514 
515             Lit l(v, false);
516             assert(solver->watches.size() > l.toInt());
517             solver->watches[l].push(Watched(i)); //Idx watch
518             solver->watches.smudge(l);
519         }
520     }
521 
522     //Don't XOR together over the sampling vars
523     //or variables that are in regular clauses
524     vector<uint32_t> to_clear_2;
525     if (solver->conf.sampling_vars) {
526         for(uint32_t outside_var: *solver->conf.sampling_vars) {
527             uint32_t outer_var = solver->map_to_with_bva(outside_var);
528             outer_var = solver->varReplacer->get_var_replaced_with_outer(outer_var);
529             uint32_t int_var = solver->map_outer_to_inter(outer_var);
530             if (int_var < solver->nVars()) {
531                 if (!seen2[int_var]) {
532                     seen2[int_var] = 1;
533                     to_clear_2.push_back(int_var);
534                     //cout << "sampling var: " << int_var+1 << endl;
535                 }
536             }
537         }
538     }
539 
540     for(const auto& ws: solver->watches) {
541         for(const auto& w: ws) {
542             if (w.isBin() && !w.red()) {
543                 uint32_t v = w.lit2().var();
544                 if (!seen2[v]) {
545                     seen2[v] = 1;
546                     to_clear_2.push_back(v);
547                 }
548             }
549         }
550     }
551 
552     for(const auto& offs: solver->longIrredCls) {
553         Clause* cl = solver->cl_alloc.ptr(offs);
554         if (cl->red() || cl->used_in_xor()) {
555             continue;
556         }
557         for(Lit l: *cl) {
558             if (!seen2[l.var()]) {
559                 seen2[l.var()] = 1;
560                 to_clear_2.push_back(l.var());
561                 //cout << "Not XORing together over var: " << l.var()+1 << endl;
562             }
563         }
564     }
565 
566     //until fixedpoint
567     bool changed = true;
568     while(changed) {
569         changed = false;
570         interesting.clear();
571         for(const Lit l: toClear) {
572             if (occcnt[l.var()] == 2 && !seen2[l.var()]) {
573                 interesting.push_back(l.var());
574             }
575         }
576 
577         while(!interesting.empty()) {
578             #ifdef SLOW_DEBUG
579             {
580                 vector<uint32_t> check;
581                 check.resize(solver->nVars(), 0);
582                 for(size_t i = 0; i < this_xors.size(); i++) {
583                     const Xor& x = this_xors[i];
584                     for(uint32_t v: x) {
585                         check[v]++;
586                     }
587                 }
588                 for(size_t i = 0; i < solver->nVars(); i++) {
589                     assert(check[i] == occcnt[i]);
590                 }
591             }
592             #endif
593 
594             //Pop and check if it can be XOR-ed together
595             const uint32_t v = interesting.back();
596             interesting.resize(interesting.size()-1);
597             if (occcnt[v] != 2)
598                 continue;
599 
600             size_t idxes[2];
601             unsigned at = 0;
602             size_t i2 = 0;
603             assert(solver->watches.size() > Lit(v, false).toInt());
604             watch_subarray ws = solver->watches[Lit(v, false)];
605 
606             //Remove the 2 indexes from the watchlist
607             for(size_t i = 0; i < ws.size(); i++) {
608                 const Watched& w = ws[i];
609                 if (!w.isIdx()) {
610                     ws[i2++] = ws[i];
611                 } else if (!this_xors[w.get_idx()].empty()) {
612                     assert(at < 2);
613                     idxes[at] = w.get_idx();
614                     at++;
615                 }
616             }
617             assert(at == 2);
618             ws.resize(i2);
619 
620             Xor& x0 = this_xors[idxes[0]];
621             Xor& x1 = this_xors[idxes[1]];
622             uint32_t clash_var;
623             uint32_t clash_num = xor_two(&x0, &x1, clash_var);
624 
625             //If they are equivalent
626             if (x0.size() == x1.size()
627                 && x0.rhs == x1.rhs
628                 && clash_num == x0.size()
629             ) {
630                 #ifdef VERBOSE_DEBUG
631                 cout << "x1: " << x0 << " -- at idx: " << idxes[0] << endl;
632                 cout << "x2: " << x1 << " -- at idx: " << idxes[1] << endl;
633                 cout << "equivalent. " << endl;
634                 #endif
635 
636                 //Update clash values & detached values
637                 x1.merge_clash(x0, seen);
638                 x1.detached |= x0.detached;
639 
640                 #ifdef VERBOSE_DEBUG
641                 cout << "after merge: " << x1 <<  " -- at idx: " << idxes[1] << endl;
642                 #endif
643 
644                 //Equivalent, so delete one
645                 x0 = Xor();
646 
647                 //Re-attach the other, remove the occur of the one we deleted
648                 solver->watches[Lit(v, false)].push(Watched(idxes[1]));
649 
650                 for(uint32_t v2: x1) {
651                     Lit l(v2, false);
652                     assert(occcnt[l.var()] >= 2);
653                     occcnt[l.var()]--;
654                     if (occcnt[l.var()] == 2 && !seen2[l.var()]) {
655                         interesting.push_back(l.var());
656                     }
657                 }
658             } else if (clash_num > 1 || x0.detached || x1.detached) {
659                 //add back to ws, can't do much
660                 ws.push(Watched(idxes[0]));
661                 ws.push(Watched(idxes[1]));
662                 continue;
663             } else {
664                 occcnt[v] -= 2;
665                 assert(occcnt[v] == 0);
666 
667                 Xor x_new(tmp_vars_xor_two, x0.rhs ^ x1.rhs, clash_var);
668                 x_new.merge_clash(x0, seen);
669                 x_new.merge_clash(x1, seen);
670                 #ifdef VERBOSE_DEBUG
671                 cout << "x1: " << x0 << " -- at idx: " << idxes[0] << endl;
672                 cout << "x2: " << x1 << " -- at idx: " << idxes[1] << endl;
673                 cout << "clashed on var: " << clash_var+1 << endl;
674                 cout << "final: " << x_new <<  " -- at idx: " << this_xors.size() << endl;
675                 #endif
676                 changed = true;
677                 this_xors.push_back(x_new);
678                 for(uint32_t v2: x_new) {
679                     Lit l(v2, false);
680                     solver->watches[l].push(Watched(this_xors.size()-1));
681                     assert(occcnt[l.var()] >= 1);
682                     if (occcnt[l.var()] == 2 && !seen2[l.var()]) {
683                         interesting.push_back(l.var());
684                     }
685                 }
686                 this_xors[idxes[0]] = Xor();
687                 this_xors[idxes[1]] = Xor();
688                 xored++;
689             }
690         }
691     }
692 
693     if (solver->conf.verbosity >= 5) {
694         cout << "c Finished XOR-ing together XORs. " << endl;
695         size_t at = 0;
696         for(const auto& x: this_xors) {
697             cout << "c XOR after xor-ing together: " << x << " -- at idx: " << at << endl;
698             at++;
699         }
700     }
701 
702     for(const Lit l: toClear) {
703         occcnt[l.var()] = 0;
704     }
705     toClear.clear();
706 
707     //Clear seen2
708     for(const auto& x: to_clear_2) {
709         seen2[x] = 0;
710     }
711 
712     solver->clean_occur_from_idx_types_only_smudged();
713     clean_xors_from_empty(this_xors);
714     double recur_time = cpuTime() - myTime;
715         if (solver->conf.verbosity) {
716         cout
717         << "c [xor-together] xored together: " << xored
718         << " orig xors: " << origsize
719         << " new xors: " << this_xors.size()
720         << solver->conf.print_times(recur_time)
721         << endl;
722     }
723 
724 
725     if (solver->sqlStats) {
726         solver->sqlStats->time_passed_min(
727             solver
728             , "xor-xor-together"
729             , recur_time
730         );
731     }
732 
733     #if defined(SLOW_DEBUG) || defined(XOR_DEBUG)
734     //Make sure none is 2.
735     assert(toClear.empty());
736     for(const Xor& x: this_xors) {
737         for(uint32_t v: x) {
738             if (occcnt[v] == 0) {
739                 toClear.push_back(Lit(v, false));
740             }
741 
742             //Don't roll around
743             occcnt[v]++;
744         }
745     }
746 
747     for(const Lit c: toClear) {
748         /*This is now possible because we don't XOR them together
749         in case they clash on more than 1 variable */
750         //assert(occcnt[c.var()] != 2);
751 
752         occcnt[c.var()] = 0;
753     }
754     toClear.clear();
755     #endif
756 
757     return solver->okay();
758 }
759 
clean_xors_from_empty(vector<Xor> & thisxors)760 void XorFinder::clean_xors_from_empty(vector<Xor>& thisxors)
761 {
762     size_t j = 0;
763     for(size_t i = 0;i < thisxors.size(); i++) {
764         Xor& x = thisxors[i];
765         if (x.size() == 0
766             && x.rhs == false
767         ) {
768             if (!x.clash_vars.empty()) {
769                 unused_xors.push_back(x);
770             }
771         } else {
772             if (solver->conf.verbosity >= 4) {
773                 cout << "c xor after clean: " << thisxors[i] << endl;
774             }
775             thisxors[j++] = thisxors[i];
776         }
777     }
778     thisxors.resize(j);
779 }
780 
add_new_truths_from_xors(vector<Xor> & this_xors,vector<Lit> * out_changed_occur)781 bool XorFinder::add_new_truths_from_xors(vector<Xor>& this_xors, vector<Lit>* out_changed_occur)
782 {
783     size_t origTrailSize  = solver->trail_size();
784     size_t origBins = solver->binTri.redBins;
785     double myTime = cpuTime();
786 
787     assert(solver->ok);
788     size_t i2 = 0;
789     for(size_t i = 0;i < this_xors.size(); i++) {
790         Xor& x = this_xors[i];
791         solver->clean_xor_vars_no_prop(x.get_vars(), x.rhs);
792         if (x.size() > 2) {
793             this_xors[i2] = this_xors[i];
794             i2++;
795             continue;
796         }
797 
798         switch(x.size() ) {
799             case 0: {
800                 if (x.rhs == true) {
801                     solver->ok = false;
802                     return false;
803                 }
804                 break;
805             }
806 
807             case 1: {
808                 Lit lit = Lit(x[0], !x.rhs);
809                 if (solver->value(lit) == l_False) {
810                     solver->ok = false;
811                 } else if (solver->value(lit) == l_Undef) {
812                     solver->enqueue(lit);
813                     if (out_changed_occur)
814                         solver->ok = solver->propagate_occur();
815                     else
816                         solver->ok = solver->propagate<true>().isNULL();
817                 }
818                 if (!solver->ok) {
819                     goto end;
820                 }
821                 break;
822             }
823 
824             case 2: {
825                 //RHS == 1 means both same is not allowed
826                 vector<Lit> lits{Lit(x[0], false), Lit(x[1], true^x.rhs)};
827                 solver->add_clause_int(lits, true, ClauseStats(), out_changed_occur != NULL);
828                 if (!solver->ok) {
829                     goto end;
830                 }
831                 lits = {Lit(x[0], true), Lit(x[1], false^x.rhs)};
832                 solver->add_clause_int(lits, true, ClauseStats(), out_changed_occur != NULL);
833                 if (!solver->ok) {
834                     goto end;
835                 }
836                 if (out_changed_occur) {
837                     solver->ok = solver->propagate_occur();
838                 } else {
839                     solver->ok = solver->propagate<true>().isNULL();
840                 }
841 
842                 if (out_changed_occur) {
843                     out_changed_occur->push_back(Lit(x[0], false));
844                     out_changed_occur->push_back(Lit(x[1], false));
845                 }
846                 break;
847             }
848 
849             default: {
850                 assert(false && "Not possible");
851             }
852         }
853     }
854     this_xors.resize(i2);
855     end:
856 
857     double add_time = cpuTime() - myTime;
858     uint32_t num_bins_added = solver->binTri.redBins - origBins;
859     uint32_t num_units_added = solver->trail_size() - origTrailSize;
860 
861     if (solver->conf.verbosity) {
862         cout
863         << "c [xor-add-lem] added unit " << num_units_added
864         << " bin " << num_bins_added
865         << solver->conf.print_times(add_time)
866         << endl;
867     }
868 
869 
870     if (solver->sqlStats) {
871         solver->sqlStats->time_passed_min(
872             solver
873             , "xor-add-new-bin-unit"
874             , add_time
875         );
876     }
877 
878     return solver->okay();
879 }
880 
xor_two(Xor const * x1_p,Xor const * x2_p,uint32_t & clash_var)881 uint32_t XorFinder::xor_two(Xor const* x1_p, Xor const* x2_p, uint32_t& clash_var)
882 {
883     tmp_vars_xor_two.clear();
884     if (x1_p->size() > x2_p->size()) {
885         std::swap(x1_p, x2_p);
886     }
887     const Xor& x1 = *x1_p;
888     const Xor& x2 = *x2_p;
889 
890     uint32_t clash_num = 0;
891     for(uint32_t v: x1) {
892         assert(seen[v] == 0);
893         seen[v] = 1;
894     }
895 
896     uint32_t i_x2;
897     bool early_abort = false;
898     for(i_x2 = 0; i_x2 < x2.size(); i_x2++) {
899         uint32_t v = x2[i_x2];
900         assert(seen[v] != 2);
901         if (seen[v] == 0) {
902             tmp_vars_xor_two.push_back(v);
903         } else {
904             clash_var = v;
905             if (clash_num > 0 &&
906                 clash_num != i_x2 //not equivalent by chance
907             ) {
908                 //early abort, it's never gonna be good
909                 clash_num++;
910                 early_abort = true;
911                 break;
912             }
913             clash_num++;
914         }
915         seen[v] = 2;
916     }
917 
918     if (!early_abort) {
919         #ifdef SLOW_DEBUG
920         uint32_t other_clash = 0;
921         #endif
922         for(uint32_t v: x1) {
923             if (seen[v] != 2) {
924                 tmp_vars_xor_two.push_back(v);
925             } else {
926                 #ifdef SLOW_DEBUG
927                 other_clash++;
928                 #endif
929             }
930             seen[v] = 0;
931         }
932         #ifdef SLOW_DEBUG
933         assert(other_clash == clash_num);
934         #endif
935     } else {
936         for(uint32_t v: x1) {
937             seen[v] = 0;
938         }
939     }
940 
941     for(uint32_t i = 0; i < i_x2; i++) {
942         seen[x2[i]] = 0;
943     }
944 
945     #ifdef SLOW_DEBUG
946     for(uint32_t v: x1) {
947         assert(seen[v] == 0);
948     }
949     #endif
950 
951     return clash_num;
952 }
953 
xor_has_interesting_var(const Xor & x)954 bool XorFinder::xor_has_interesting_var(const Xor& x)
955 {
956     for(uint32_t v: x) {
957         if (solver->seen[v] > 1) {
958             return true;
959         }
960     }
961     return false;
962 }
963 
mem_used() const964 size_t XorFinder::mem_used() const
965 {
966     size_t mem = 0;
967     mem += xors.capacity()*sizeof(Xor);
968 
969     //Temporary
970     mem += tmpClause.capacity()*sizeof(Lit);
971     mem += varsMissing.capacity()*sizeof(uint32_t);
972 
973     return mem;
974 }
975 
grab_mem()976 void XorFinder::grab_mem()
977 {
978     occcnt.clear();
979     occcnt.resize(solver->nVars(), 0);
980 }
981 
print_short(const Solver * solver,double time_remain) const982 void XorFinder::Stats::print_short(const Solver* solver, double time_remain) const
983 {
984     cout
985     << "c [occ-xor] found " << std::setw(6) << foundXors
986     ;
987     if (foundXors > 0) {
988         cout
989         << " avg sz " << std::setw(3) << std::fixed << std::setprecision(1)
990         << float_div(sumSizeXors, foundXors)
991         << " min sz " << std::setw(2) << std::fixed << std::setprecision(1)
992         << minsize
993         << " max sz " << std::setw(2) << std::fixed << std::setprecision(1)
994         << maxsize;
995     }
996     cout
997     << solver->conf.print_times(findTime, time_outs, time_remain)
998     << endl;
999 }
1000 
operator +=(const XorFinder::Stats & other)1001 XorFinder::Stats& XorFinder::Stats::operator+=(const XorFinder::Stats& other)
1002 {
1003     //Time
1004     findTime += other.findTime;
1005 
1006     //XOR
1007     foundXors += other.foundXors;
1008     sumSizeXors += other.sumSizeXors;
1009 
1010     //Usefulness
1011     time_outs += other.time_outs;
1012 
1013     return *this;
1014 }
1015