1 /*++
2 Copyright (c) 2011 Microsoft Corporation
3
4 Module Name:
5
6 polynomial.cpp
7
8 Abstract:
9
10 Goodies for creating and handling polynomials.
11
12 Author:
13
14 Leonardo (leonardo) 2011-11-15
15
16 Notes:
17
18 --*/
19 #include "math/polynomial/polynomial.h"
20 #include "util/vector.h"
21 #include "util/chashtable.h"
22 #include "util/small_object_allocator.h"
23 #include "util/id_gen.h"
24 #include "util/buffer.h"
25 #include "util/scoped_ptr_vector.h"
26 #include "math/polynomial/upolynomial_factorization.h"
27 #include "math/polynomial/polynomial_primes.h"
28 #include "util/permutation.h"
29 #include "math/polynomial/algebraic_numbers.h"
30 #include "util/mpzzp.h"
31 #include "util/timeit.h"
32 #include "math/polynomial/linear_eq_solver.h"
33 #include "util/scoped_numeral_buffer.h"
34 #include "util/ref_buffer.h"
35 #include "util/common_msgs.h"
36 #include <memory>
37
38 namespace polynomial {
39
factor_params()40 factor_params::factor_params():
41 m_max_p(UINT_MAX),
42 m_p_trials(1),
43 m_max_search_size(UINT_MAX) {
44 }
45
factor_params(unsigned max_p,unsigned p_trials,unsigned max_search_size)46 factor_params::factor_params(unsigned max_p, unsigned p_trials, unsigned max_search_size):
47 m_max_p(max_p),
48 m_p_trials(p_trials),
49 m_max_search_size(max_search_size) {
50 }
51
updt_params(params_ref const & p)52 void factor_params::updt_params(params_ref const & p) {
53 m_max_p = p.get_uint("max_prime", UINT_MAX);
54 m_p_trials = p.get_uint("num_primes", 1);
55 m_max_search_size = p.get_uint("max_search_size", UINT_MAX);
56 }
57
get_param_descrs(param_descrs & r)58 void factor_params::get_param_descrs(param_descrs & r) {
59 r.insert("max_search_size", CPK_UINT, "(default: infty) Z3 polynomial factorization is composed of three steps: factorization in GF(p), lifting and search. This parameter can be used to limit the search space.");
60 r.insert("max_prime", CPK_UINT, "(default: infty) Z3 polynomial factorization is composed of three steps: factorization in GF(p), lifting and search. This parameter limits the maximum prime number p to be used in the first step.");
61 r.insert("num_primes", CPK_UINT, "(default: 1) Z3 polynomial factorization is composed of three steps: factorization in GF(p), lifting and search. The search space may be reduced by factoring the polynomial in different GF(p)'s. This parameter specify the maximum number of finite factorizations to be considered, before lifiting and searching.");
62 }
63
64 typedef ptr_vector<monomial> monomial_vector;
65
display(std::ostream & out) const66 void var2degree::display(std::ostream & out) const {
67 bool first = true;
68 out << "[";
69 for (unsigned i = 0; i < m_var2degree.size(); ++ i) {
70 if (!m_var2degree.empty()) {
71 if (!first) {
72 out << ",";
73 }
74 out << "x" << i << "^" << m_var2degree[i];
75 if (first) {
76 first = false;
77 }
78 }
79 }
80 out << "]";
81 }
82
83 // -----------------------------------
84 //
85 // Monomials
86 //
87 // -----------------------------------
88
89 /**
90 \brief power: var + exponent
91 */
92 class power : public std::pair<var, unsigned> {
93 public:
power()94 power():std::pair<var, unsigned>() {}
power(var v,unsigned d)95 power(var v, unsigned d):std::pair<var, unsigned>(v, d) {}
get_var() const96 var get_var() const { return first; }
degree() const97 unsigned degree() const { return second; }
degree()98 unsigned & degree() { return second; }
set_var(var x)99 void set_var(var x) { first = x; }
100
101 struct lt_var {
operator ()polynomial::power::lt_var102 bool operator()(power const & p1, power const & p2) {
103 // CMW: The assertion below does not hold on macOS, because
104 // their implementation of std::sort will try to compare
105 // two items at the same index instead of comparing
106 // the indices directly. I suspect that the purpose of
107 // this assertion was to make sure that there are
108 // no duplicates, so I replaced it with a new assertion at
109 // the end of var_degrees(...).
110
111 // SASSERT(p1.get_var() != p2.get_var());
112
113 return p1.get_var() < p2.get_var();
114 }
115 };
116
117 struct lt_degree {
operator ()polynomial::power::lt_degree118 bool operator()(power const & p1, power const & p2) {
119 return p1.degree() < p2.degree();
120 }
121 };
122 };
123
124
operator <<(std::ostream & out,power const & p)125 std::ostream & operator<<(std::ostream & out, power const & p) {
126 out << "x" << p.get_var();
127 if (p.degree() != 1)
128 out << "^" << p.degree();
129 return out;
130 }
131
132 /**
133 \brief Return true if the variables in pws are sorted in increasing order and are distinct.
134 */
is_valid_power_product(unsigned sz,power const * pws)135 bool is_valid_power_product(unsigned sz, power const * pws) {
136 for (unsigned i = 1; i < sz; i++) {
137 if (pws[i-1].get_var() >= pws[i].get_var())
138 return false;
139 }
140 return true;
141 }
142
143 /**
144 \brief Return total degree of the given power product.
145 */
power_product_total_degree(unsigned sz,power const * pws)146 unsigned power_product_total_degree(unsigned sz, power const * pws) {
147 unsigned r = 0;
148 for (unsigned i = 0; i < sz; i++)
149 r += pws[i].degree();
150 return r;
151 }
152
153 /**
154 \brief Monomials (power products)
155 */
156 class monomial {
157 unsigned m_ref_count;
158 unsigned m_id; //!< unique id
159 unsigned m_total_degree; //!< total degree of the monomial
160 unsigned m_size; //!< number of powers
161 unsigned m_hash;
162 power m_powers[0];
163 friend class tmp_monomial;
164
sort()165 void sort() {
166 std::sort(m_powers, m_powers + m_size, power::lt_var());
167 }
168 public:
hash_core(unsigned sz,power const * pws)169 static unsigned hash_core(unsigned sz, power const * pws) {
170 return string_hash(reinterpret_cast<char*>(const_cast<power*>(pws)), sz*sizeof(power), 11);
171 }
172
173 struct hash_proc {
operator ()polynomial::monomial::hash_proc174 unsigned operator()(monomial const * m) const {
175 return m->m_hash;
176 }
177 };
178
179 struct eq_proc {
operator ()polynomial::monomial::eq_proc180 bool operator()(monomial const * m1, monomial const * m2) const {
181 if (m1->size() != m2->size() || m1->hash() != m2->hash())
182 return false;
183 // m_total_degree must not be used as a filter, because it is not updated in temporary monomials.
184 for (unsigned i = 0; i < m1->m_size; i++) {
185 if (m1->get_power(i) != m2->get_power(i))
186 return false;
187 }
188
189 return true;
190 }
191 };
192
get_obj_size(unsigned sz)193 static unsigned get_obj_size(unsigned sz) { return sizeof(monomial) + sz * sizeof(power); }
194
monomial(unsigned id,unsigned sz,power const * pws,unsigned h)195 monomial(unsigned id, unsigned sz, power const * pws, unsigned h):
196 m_ref_count(0),
197 m_id(id),
198 m_total_degree(0),
199 m_size(sz),
200 m_hash(h) {
201 for (unsigned i = 0; i < sz; i ++) {
202 power const & pw = pws[i];
203 m_powers[i] = pw;
204 SASSERT(i == 0 || get_var(i) > get_var(i-1));
205 SASSERT(degree(i) > 0);
206 m_total_degree += degree(i);
207 }
208 }
209
hash() const210 unsigned hash() const { return m_hash; }
211
ref_count() const212 unsigned ref_count() const { return m_ref_count; }
213
inc_ref()214 void inc_ref() { m_ref_count++; }
215
dec_ref()216 void dec_ref() { SASSERT(m_ref_count > 0); m_ref_count--; }
217
is_valid() const218 bool is_valid() const {
219 return is_valid_power_product(m_size, m_powers) && power_product_total_degree(m_size, m_powers) == m_total_degree;
220 }
221
id() const222 unsigned id() const { return m_id; }
223
size() const224 unsigned size() const { return m_size; }
225
total_degree() const226 unsigned total_degree() const { return m_total_degree; }
227
get_power(unsigned idx) const228 power const & get_power(unsigned idx) const { SASSERT(idx < size()); return m_powers[idx]; }
229
get_powers() const230 power const * get_powers() const { return m_powers; }
231
get_var(unsigned idx) const232 var get_var(unsigned idx) const { return get_power(idx).get_var(); }
233
degree(unsigned idx) const234 unsigned degree(unsigned idx) const { return get_power(idx).degree(); }
235
max_var() const236 var max_var() const {
237 if (m_size == 0)
238 return null_var;
239 return get_var(m_size - 1);
240 }
241
max_var_degree() const242 unsigned max_var_degree() const {
243 if (m_size == 0)
244 return 0;
245 return degree(m_size - 1);
246 }
247
248 #define SMALL_MONOMIAL 8
249
index_of(var x) const250 unsigned index_of(var x) const {
251 if (m_size == 0)
252 return UINT_MAX;
253 unsigned last = m_size - 1;
254 if (get_var(last) == x)
255 return last;
256 if (m_size < SMALL_MONOMIAL) {
257 // use linear search for small monomials
258 // search backwards since we usually ask for the degree of "big" variables
259 for (unsigned i = last; i-- > 0; ) {
260 if (get_var(i) == x)
261 return i;
262 }
263 return UINT_MAX;
264 }
265 else {
266 // use binary search for big monomials
267 int low = 0;
268 int high = last;
269 while (true) {
270 int mid = low + ((high - low)/2);
271 var x_mid = get_var(mid);
272 if (x > x_mid) {
273 low = mid + 1;
274 }
275 else if (x < x_mid) {
276 high = mid - 1;
277 }
278 else {
279 return mid;
280 }
281 if (low > high)
282 return UINT_MAX;
283 }
284 }
285 }
286
degree_of(var x) const287 unsigned degree_of(var x) const {
288 unsigned pos = index_of(x);
289 if (pos == UINT_MAX)
290 return 0;
291 return degree(pos);
292 }
293
294
295 // Given the subset S of variables that are smaller than x,
296 // then return the maximal one.
max_smaller_than_core(var x) const297 var max_smaller_than_core(var x) const {
298 if (m_size == 0)
299 return null_var;
300 if (m_size < SMALL_MONOMIAL) {
301 // use linear search for small monomials
302 // search backwards since we usually ask for the degree of "big" variables
303 unsigned i = m_size;
304 while (i > 0) {
305 --i;
306 if (get_var(i) < x)
307 return get_var(i);
308 }
309 return null_var;
310 }
311 else {
312 // use binary search for big monomials
313 int low = 0;
314 int high = m_size-1;
315 if (x <= get_var(low)) {
316 return null_var;
317 }
318 if (x > get_var(high)) {
319 return get_var(high);
320 }
321 if (x == get_var(high)) {
322 SASSERT(high > 0);
323 return get_var(high-1);
324 }
325 while (true) {
326 SASSERT(0 <= low && high < static_cast<int>(m_size));
327 SASSERT(get_var(low) < x);
328 SASSERT(x < get_var(high));
329 SASSERT(low < high);
330 if (high == low + 1) {
331 SASSERT(get_var(low) < x);
332 SASSERT(x < get_var(low+1));
333 return get_var(low);
334 }
335 SASSERT(high > low + 1);
336 int mid = low + ((high - low)/2);
337 SASSERT(low < mid && mid < high);
338 var x_mid = get_var(mid);
339 if (x_mid == x) {
340 SASSERT(low < mid && mid < high && high < static_cast<int>(m_size));
341 SASSERT(get_var(mid-1) < x && x == get_var(mid));
342 return get_var(mid-1);
343 }
344 if (x < x_mid) {
345 high = mid;
346 }
347 else {
348 SASSERT(x_mid < x);
349 low = mid;
350 }
351 }
352 }
353 }
354
max_smaller_than(var x) const355 var max_smaller_than(var x) const {
356 SASSERT(x != null_var);
357 var y = max_smaller_than_core(x);
358 DEBUG_CODE({
359 bool found = false;
360 for (unsigned i = 0; i < m_size; i++) {
361 if (get_var(i) < x) {
362 CTRACE("poly_bug", !(y != null_var && get_var(i) <= y),
363 tout << "m: "; display(tout); tout << "\n";
364 tout << "x: " << x << "\n";
365 tout << "y: " << y << "\n";
366 tout << "i: " << i << "\n";
367 tout << "get_var(i): " << get_var(i) << "\n";);
368 SASSERT(y != null_var && get_var(i) <= y);
369 }
370 if (get_var(i) == y)
371 found = true;
372 }
373 SASSERT(y == null_var || (y < x && found));
374 });
375 return y;
376 }
377
display(std::ostream & out,display_var_proc const & proc=display_var_proc (),bool use_star=false) const378 std::ostream& display(std::ostream & out, display_var_proc const & proc = display_var_proc(), bool use_star = false) const {
379 if (m_size == 0) {
380 out << "1";
381 return out;
382 }
383 for (unsigned i = 0; i < m_size; i++) {
384 if (i > 0) {
385 if (use_star)
386 out << "*";
387 else
388 out << " ";
389 }
390 proc(out, get_var(i));
391 if (degree(i) > 1)
392 out << "^" << degree(i);
393 }
394 return out;
395 }
396
display_smt2(std::ostream & out,display_var_proc const & proc=display_var_proc ()) const397 void display_smt2(std::ostream & out, display_var_proc const & proc = display_var_proc()) const {
398 if (m_size == 0) {
399 out << "1";
400 }
401 else if (m_size == 1 && degree(0) == 1) {
402 proc(out, get_var(0));
403 }
404 else {
405 out << "(*";
406 for (unsigned i = 0; i < m_size; i++) {
407 var x = get_var(i);
408 unsigned k = degree(i);
409 SASSERT(k > 0);
410 for (unsigned j = 0; j < k; j++) {
411 out << " ";
412 proc(out, x);
413 }
414 }
415 out << ")";
416 }
417 }
418
is_unit() const419 bool is_unit() const { return m_size == 0; }
420
421 /**
422 \brief Return true if the degree of every variable is even.
423 */
is_power_of_two() const424 bool is_power_of_two() const {
425 for (unsigned i = 0; i < m_size; i++) {
426 if (degree(i) % 2 == 1)
427 return false;
428 }
429 return true;
430 }
431
is_square() const432 bool is_square() const {
433 for (unsigned i = 0; i < m_size; i++) {
434 if (degree(i) % 2 != 0)
435 return false;
436 }
437 return true;
438 }
439
rename(unsigned sz,var const * xs)440 void rename(unsigned sz, var const * xs) {
441 for (unsigned i = 0; i < m_size; i++) {
442 power & pw = m_powers[i];
443 pw.set_var(xs[pw.get_var()]);
444 }
445 sort();
446 m_hash = hash_core(m_size, m_powers);
447 }
448 };
449
swap(monomial * & m1,monomial * & m2)450 inline void swap(monomial * & m1, monomial * & m2) { std::swap(m1, m2); }
451
452 typedef chashtable<monomial*, monomial::hash_proc, monomial::eq_proc> monomial_table;
453
454 /**
455 \brief Mapping from monomials to positions.
456 */
457 class monomial2pos {
458 unsigned_vector m_m2pos;
459 public:
get(monomial const * m)460 unsigned get(monomial const * m) {
461 unsigned id = m->id();
462 m_m2pos.reserve(id+1, UINT_MAX);
463 return m_m2pos[id];
464 }
465
reset(monomial const * m)466 void reset(monomial const * m) {
467 unsigned id = m->id();
468 SASSERT(id < m_m2pos.size());
469 m_m2pos[id] = UINT_MAX;
470 }
471
set(monomial const * m,unsigned pos)472 void set(monomial const * m, unsigned pos) {
473 unsigned id = m->id();
474 m_m2pos.reserve(id+1, UINT_MAX);
475 SASSERT(m_m2pos[id] == UINT_MAX);
476 m_m2pos[id] = pos;
477 }
478
479 /**
480 \brief Save the position of the monomials in p.
481 */
482 template<typename Poly>
set(Poly const * p)483 void set(Poly const * p) {
484 unsigned sz = p->size();
485 for (unsigned i = 0; i < sz; i++) {
486 set(p->m(i), i);
487 }
488 }
489
490 /**
491 \brief Undo the effects of save_pos.
492 */
493 template<typename Poly>
reset(Poly const * p)494 void reset(Poly const * p) {
495 unsigned sz = p->size();
496 for (unsigned i = 0; i < sz; i++) {
497 reset(p->m(i));
498 }
499 }
500 };
501
502
503 #define TMP_INITIAL_CAPACITY 128
504
505 /**
506 \brief Wrapper for temporary monomials.
507 */
508 class tmp_monomial {
509 monomial * m_ptr;
510 unsigned m_capacity; //!< maximum number of arguments supported by m_ptr;
511
allocate(unsigned capacity)512 monomial * allocate(unsigned capacity) {
513 void * mem = memory::allocate(monomial::get_obj_size(capacity));
514 return new (mem) monomial(UINT_MAX, 0, nullptr, 0);
515 }
516
deallocate(monomial * ptr,unsigned capacity)517 void deallocate(monomial * ptr, unsigned capacity) {
518 memory::deallocate(ptr);
519 }
520
increase_capacity(unsigned new_capacity)521 void increase_capacity(unsigned new_capacity) {
522 SASSERT(new_capacity > m_capacity);
523 deallocate(m_ptr, m_capacity);
524 m_ptr = allocate(new_capacity);
525 m_capacity = new_capacity;
526 }
527
expand_capacity(unsigned new_capacity)528 void expand_capacity(unsigned new_capacity) {
529 SASSERT(new_capacity > m_capacity);
530 monomial * new_ptr = allocate(new_capacity);
531 new_ptr->m_size = m_ptr->m_size;
532 std::uninitialized_copy(m_ptr->m_powers, m_ptr->m_powers + m_ptr->m_size, new_ptr->m_powers);
533 deallocate(m_ptr, m_capacity);
534 m_ptr = new_ptr;
535 m_capacity = new_capacity;
536 }
537 public:
tmp_monomial()538 tmp_monomial():
539 m_ptr(allocate(TMP_INITIAL_CAPACITY)),
540 m_capacity(TMP_INITIAL_CAPACITY) {
541 }
542
~tmp_monomial()543 ~tmp_monomial() {
544 deallocate(m_ptr, m_capacity);
545 }
546
init(unsigned sz,power const * pws)547 void init(unsigned sz, power const * pws) {
548 if (sz > m_capacity)
549 increase_capacity(sz * 2);
550 SASSERT(sz < m_capacity);
551 m_ptr->m_size = sz;
552 std::uninitialized_copy(pws, pws + sz, m_ptr->m_powers);
553 }
554
reset()555 void reset() {
556 m_ptr->m_size = 0;
557 }
558
size() const559 unsigned size() const {
560 return m_ptr->m_size;
561 }
562
push_back(power const & pw)563 void push_back(power const & pw) {
564 if (m_ptr->m_size >= m_capacity)
565 expand_capacity(m_ptr->m_size * 2);
566 m_ptr->m_powers[m_ptr->m_size] = pw;
567 m_ptr->m_size++;
568 }
569
get_ptr()570 monomial * get_ptr() {
571 unsigned sz = m_ptr->m_size;
572 m_ptr->m_hash = monomial::hash_core(sz, m_ptr->m_powers);
573 return m_ptr;
574 }
575
reserve(unsigned capacity)576 void reserve(unsigned capacity) {
577 if (capacity > m_capacity)
578 increase_capacity(capacity * 2);
579 }
580
set_size(unsigned sz)581 void set_size(unsigned sz) {
582 SASSERT(sz <= m_capacity);
583 m_ptr->m_size = sz;
584 }
585
set_power(unsigned idx,power const & pw)586 void set_power(unsigned idx, power const & pw) {
587 SASSERT(idx < m_capacity);
588 m_ptr->m_powers[idx] = pw;
589 }
590
get_power(unsigned idx) const591 power const & get_power(unsigned idx) const { return m_ptr->m_powers[idx]; }
592
get_powers() const593 power const * get_powers() const { return m_ptr->m_powers; }
594 };
595
596 /**
597 \brief Compare m1 and m2 using a lexicographical order
598
599 Return
600 - -1 if m1 <_lex m2,
601 - 0 if m1 = m2,
602 - 1 if m1 >_lex m2
603
604 The biggest variable dominates
605 x3^3 > x3^2 x1^2 > x3 x2^2 x_1 > x1^3
606
607 Remark: in out representation the biggest variable is in the last position.
608 */
lex_compare(monomial const * m1,monomial const * m2)609 int lex_compare(monomial const * m1, monomial const * m2) {
610 if (m1 == m2)
611 return 0;
612 int sz1 = m1->size();
613 int sz2 = m2->size();
614 int idx1 = sz1 - 1;
615 int idx2 = sz2 - 1;
616 while (idx1 >= 0 && idx2 >= 0) {
617 power const & pw1 = m1->get_power(idx1);
618 power const & pw2 = m2->get_power(idx2);
619 if (pw1.get_var() == pw2.get_var()) {
620 if (pw1.degree() == pw2.degree()) {
621 idx1--;
622 idx2--;
623 continue;
624 }
625 return pw1.degree() < pw2.degree() ? -1 : 1;
626 }
627 return pw1.get_var() > pw2.get_var() ? 1 : -1;
628 }
629 SASSERT(idx1 >= 0 || idx2 >= 0);
630 SASSERT(idx1 < 0 || idx2 < 0);
631 return idx1 < 0 ? -1 : 1;
632 }
633
634 /**
635 Similar to lex_compare, but min_var is assumed to be the minimal variable.
636 */
lex_compare2(monomial const * m1,monomial const * m2,var min_var)637 int lex_compare2(monomial const * m1, monomial const * m2, var min_var) {
638 if (m1 == m2)
639 return 0;
640 int sz1 = m1->size();
641 int sz2 = m2->size();
642 int idx1 = sz1 - 1;
643 int idx2 = sz2 - 1;
644 unsigned min_var_degree1 = 0;
645 unsigned min_var_degree2 = 0;
646 while (idx1 >= 0 && idx2 >= 0) {
647 power const & pw1 = m1->get_power(idx1);
648 power const & pw2 = m2->get_power(idx2);
649 if (pw1.get_var() == min_var) {
650 min_var_degree1 = pw1.degree();
651 idx1--;
652 if (pw2.get_var() == min_var) {
653 min_var_degree2 = pw2.degree();
654 idx2--;
655 }
656 continue;
657 }
658 if (pw2.get_var() == min_var) {
659 min_var_degree2 = pw2.degree();
660 idx2--;
661 continue;
662 }
663 if (pw1.get_var() == pw2.get_var()) {
664 if (pw1.degree() == pw2.degree()) {
665 idx1--;
666 idx2--;
667 continue;
668 }
669 return pw1.degree() < pw2.degree() ? -1 : 1;
670 }
671 return pw1.get_var() > pw2.get_var() ? 1 : -1;
672 }
673 if (idx1 == idx2) {
674 SASSERT(min_var_degree1 != min_var_degree2);
675 return min_var_degree1 < min_var_degree2 ? -1 : 1;
676 }
677 return idx1 < 0 ? -1 : 1;
678 }
679
680 struct lex_lt2 {
681 var m_min;
lex_lt2polynomial::lex_lt2682 lex_lt2(var m):m_min(m) {}
operator ()polynomial::lex_lt2683 bool operator()(monomial * m1, monomial * m2) const {
684 TRACE("lex_bug", tout << "min: x" << m_min << "\n"; m1->display(tout); tout << "\n"; m2->display(tout); tout << "\n";);
685 return lex_compare2(m1, m2, m_min) < 0;
686 }
687 };
688
689 /**
690 \brief Compare m1 and m2 using a graded lexicographical order
691
692 \see lex_compare
693 */
graded_lex_compare(monomial const * m1,monomial const * m2)694 int graded_lex_compare(monomial const * m1, monomial const * m2) {
695 unsigned t1 = m1->total_degree();
696 unsigned t2 = m2->total_degree();
697 if (t1 == t2)
698 return lex_compare(m1, m2);
699 else
700 return t1 < t2 ? -1 : 1;
701 }
702
703 /**
704 \brief Compare submonomials m1[start1, end1) and m2[start2, end2) using reverse lexicographical order
705 */
rev_lex_compare(monomial const * m1,unsigned start1,unsigned end1,monomial const * m2,unsigned start2,unsigned end2)706 int rev_lex_compare(monomial const * m1, unsigned start1, unsigned end1, monomial const * m2, unsigned start2, unsigned end2) {
707 SASSERT(end1 >= start1);
708 SASSERT(end2 >= start2);
709 unsigned idx1 = end1;
710 unsigned idx2 = end2;
711 while(idx1 > start1 && idx2 > start2) {
712 --idx1;
713 --idx2;
714 power const & pw1 = m1->get_power(idx1);
715 power const & pw2 = m2->get_power(idx2);
716 if (pw1.get_var() == pw2.get_var()) {
717 if (pw1.degree() == pw2.degree()) {
718 // Remark: the submonomials have the same total degree, but they are not equal. So, idx1 > 0 and idx2 > 0.
719 SASSERT(idx1 > start1 && idx2 > start2);
720 continue;
721 }
722 return pw1.degree() > pw2.degree() ? -1 : 1;
723 }
724 return pw1.get_var() > pw2.get_var() ? -1 : 1;
725 }
726 SASSERT(idx1 == start1 || idx2 == start2);
727 if (idx1 == start1)
728 return idx2 == start2 ? 0 : -1;
729 SASSERT(idx2 == start2 && idx1 != start1);
730 return 1;
731 }
732
733 /**
734 \brief Compare m1 and m2 using reverse lexicographical order.
735
736 \see lex_compare
737 */
rev_lex_compare(monomial const * m1,monomial const * m2)738 int rev_lex_compare(monomial const * m1, monomial const * m2) {
739 if (m1 == m2)
740 return 0;
741 return rev_lex_compare(m1, 0, m1->size(), m2, 0, m2->size());
742 }
743
744 /**
745 \brief Compare m1 and m2 using graded reverse lexicographical order.
746
747 \see lex_compare
748 */
graded_rev_lex_compare(monomial const * m1,monomial const * m2)749 int graded_rev_lex_compare(monomial const * m1, monomial const * m2) {
750 unsigned t1 = m1->total_degree();
751 unsigned t2 = m2->total_degree();
752 if (t1 == t2)
753 return rev_lex_compare(m1, m2);
754 else
755 return t1 < t2 ? -1 : 1;
756 }
757
758 struct graded_lex_gt {
operator ()polynomial::graded_lex_gt759 bool operator()(monomial const * m1, monomial const * m2) { return graded_lex_compare(m1, m2) < 0; }
760 };
761
762 /**
763 \brief
764 */
765 class monomial_manager {
766 unsigned m_ref_count;
767 small_object_allocator * m_allocator;
768 bool m_own_allocator;
769 monomial_table m_monomials;
770 id_gen m_mid_gen; // id generator for monomials
771 unsigned m_next_var;
772 monomial * m_unit;
773 tmp_monomial m_mk_tmp;
774 tmp_monomial m_tmp1;
775 tmp_monomial m_tmp2;
776 tmp_monomial m_tmp3;
777 svector<power> m_powers_tmp;
778 public:
monomial_manager(small_object_allocator * a=nullptr)779 monomial_manager(small_object_allocator * a = nullptr) {
780 m_ref_count = 0;
781 m_next_var = 0;
782 if (a == nullptr) {
783 m_allocator = alloc(small_object_allocator, "polynomial");
784 m_own_allocator = true;
785 }
786 else {
787 m_allocator = a;
788 m_own_allocator = false;
789 }
790 m_unit = mk_monomial(0, static_cast<power const *>(nullptr));
791 inc_ref(m_unit);
792 }
793
~monomial_manager()794 ~monomial_manager() {
795 dec_ref(m_unit);
796 CTRACE("polynomial", !m_monomials.empty(),
797 tout << "monomials leaked (can happen during cancelation)\n";
798 for (auto * m : m_monomials) {
799 m->display(tout << m->id() << " " << m->ref_count() << " ") << "\n";
800 });
801 for (monomial* m : m_monomials) {
802 unsigned obj_sz = monomial::get_obj_size(m->size());
803 m_allocator->deallocate(obj_sz, m);
804 }
805 m_monomials.reset();
806 if (m_own_allocator)
807 dealloc(m_allocator);
808 }
809
inc_ref()810 void inc_ref() {
811 m_ref_count++;
812 }
813
dec_ref()814 void dec_ref() {
815 SASSERT(m_ref_count > 0);
816 m_ref_count--;
817 if (m_ref_count == 0)
818 dealloc(this);
819 }
820
allocator()821 small_object_allocator & allocator() { return *m_allocator; }
822
mk_var()823 var mk_var() {
824 var r = m_next_var;
825 m_next_var++;
826 return r;
827 }
828
num_vars() const829 unsigned num_vars() const {
830 return m_next_var;
831 }
832
is_valid(var x) const833 bool is_valid(var x) const {
834 return x < m_next_var;
835 }
836
del(monomial * m)837 void del(monomial * m) {
838 unsigned obj_sz = monomial::get_obj_size(m->size());
839 m_monomials.erase(m);
840 m_mid_gen.recycle(m->id());
841 m_allocator->deallocate(obj_sz, m);
842 }
843
inc_ref(monomial * m)844 void inc_ref(monomial * m) {
845 m->inc_ref();
846 }
847
dec_ref(monomial * m)848 void dec_ref(monomial * m) {
849 m->dec_ref();
850 if (m->ref_count() == 0)
851 del(m);
852 }
853
mk_unit()854 monomial * mk_unit() { return m_unit; }
855
mk_monomial(tmp_monomial & tmp)856 monomial * mk_monomial(tmp_monomial & tmp) {
857 monomial * tmp_ptr = tmp.get_ptr();
858 monomial * & m = m_monomials.insert_if_not_there(tmp_ptr);
859 if (m != tmp_ptr)
860 return m;
861 void * mem = m_allocator->allocate(monomial::get_obj_size(tmp_ptr->size()));
862 unsigned id = m_mid_gen.mk();
863 monomial * r = new (mem) monomial(id, tmp_ptr->size(), tmp_ptr->get_powers(), tmp_ptr->hash());
864 m = r;
865 SASSERT(m_monomials.contains(r));
866 SASSERT(*(m_monomials.find_core(r)) == r);
867 return r;
868 }
869
mk_monomial(unsigned sz,power const * pws)870 monomial * mk_monomial(unsigned sz, power const * pws) {
871 SASSERT(is_valid_power_product(sz, pws));
872 m_mk_tmp.init(sz, pws);
873 return mk_monomial(m_mk_tmp);
874 }
875
convert(monomial const * src)876 monomial * convert(monomial const * src) {
877 unsigned sz = src->size();
878 for (unsigned i = 0; i < sz; i++) {
879 var x = src->get_var(i);
880 while (x >= num_vars()) {
881 mk_var();
882 }
883 SASSERT(x < num_vars());
884 }
885 return mk_monomial(src->size(), src->get_powers());
886 }
887
mk_monomial(var x)888 monomial * mk_monomial(var x) {
889 SASSERT(is_valid(x));
890 power pw(x, 1);
891 return mk_monomial(1, &pw);
892 }
893
mk_monomial(var x,unsigned k)894 monomial * mk_monomial(var x, unsigned k) {
895 if (k == 0)
896 return m_unit;
897 SASSERT(is_valid(x));
898 power pw(x, k);
899 return mk_monomial(1, &pw);
900 }
901
mk_monomial(unsigned sz,var * xs)902 monomial * mk_monomial(unsigned sz, var * xs) {
903 if (sz == 0)
904 return m_unit;
905 if (sz == 1)
906 return mk_monomial(xs[0]);
907 m_powers_tmp.reset();
908 std::sort(xs, xs+sz);
909 SASSERT(is_valid(xs[0]));
910 m_powers_tmp.push_back(power(xs[0], 1));
911 for (unsigned i = 1; i < sz; i++) {
912 var x = xs[i];
913 SASSERT(is_valid(x));
914 power & last = m_powers_tmp.back();
915 if (x == last.get_var())
916 last.degree()++;
917 else
918 m_powers_tmp.push_back(power(x, 1));
919 }
920 return mk_monomial(m_powers_tmp.size(), m_powers_tmp.data());
921 }
922
mul(unsigned sz1,power const * pws1,unsigned sz2,power const * pws2)923 monomial * mul(unsigned sz1, power const * pws1, unsigned sz2, power const * pws2) {
924 SASSERT(is_valid_power_product(sz1, pws1));
925 SASSERT(is_valid_power_product(sz2, pws2));
926 tmp_monomial & product_tmp = m_tmp1;
927 product_tmp.reserve(sz1 + sz2); // product has at most sz1 + sz2 powers
928 unsigned i1 = 0, i2 = 0;
929 unsigned j = 0;
930 while (true) {
931 if (i1 == sz1) {
932 // copy 2
933 for (; i2 < sz2; i2++, j++)
934 product_tmp.set_power(j, pws2[i2]);
935 break;
936 }
937 if (i2 == sz2) {
938 // copy 1
939 for (; i1 < sz1; i1++, j++)
940 product_tmp.set_power(j, pws1[i1]);
941 break;
942 }
943 power const & pw1 = pws1[i1];
944 power const & pw2 = pws2[i2];
945 unsigned v1 = pw1.get_var();
946 unsigned v2 = pw2.get_var();
947 if (v1 == v2) {
948 product_tmp.set_power(j, power(v1, pw1.degree() + pw2.degree()));
949 i1++;
950 i2++;
951 }
952 else if (v1 < v2) {
953 product_tmp.set_power(j, pw1);
954 i1++;
955 }
956 else {
957 SASSERT(v1 > v2);
958 product_tmp.set_power(j, pw2);
959 i2++;
960 }
961 j++;
962 }
963 product_tmp.set_size(j);
964 TRACE("monomial_mul_bug",
965 tout << "before mk_monomial\n";
966 tout << "pws1: "; for (unsigned i = 0; i < sz1; i++) tout << pws1[i] << " "; tout << "\n";
967 tout << "pws2: "; for (unsigned i = 0; i < sz2; i++) tout << pws2[i] << " "; tout << "\n";
968 tout << "product_tmp: "; for (unsigned i = 0; i < product_tmp.size(); i++) tout << product_tmp.get_power(i) << " ";
969 tout << "\n";);
970 monomial * r = mk_monomial(product_tmp);
971 TRACE("monomial_mul_bug",
972 tout << "j: " << j << "\n";
973 tout << "r: "; r->display(tout); tout << "\n";
974 tout << "pws1: "; for (unsigned i = 0; i < sz1; i++) tout << pws1[i] << " "; tout << "\n";
975 tout << "pws2: "; for (unsigned i = 0; i < sz2; i++) tout << pws2[i] << " "; tout << "\n";
976 tout << "product_tmp: "; for (unsigned i = 0; i < product_tmp.size(); i++) tout << product_tmp.get_power(i) << " ";
977 tout << "\n";);
978 SASSERT(r->is_valid());
979 SASSERT(r->total_degree() == power_product_total_degree(sz1, pws1) + power_product_total_degree(sz2, pws2));
980 return r;
981 }
982
mul(monomial const * m1,monomial const * m2)983 monomial * mul(monomial const * m1, monomial const * m2) {
984 if (m1 == m_unit)
985 return const_cast<monomial*>(m2);
986 if (m2 == m_unit)
987 return const_cast<monomial*>(m1);
988 return mul(m1->size(), m1->get_powers(), m2->size(), m2->get_powers());
989 }
990
991
992 template<bool STORE_RESULT>
div_core(unsigned sz1,power const * pws1,unsigned sz2,power const * pws2,tmp_monomial & r)993 bool div_core(unsigned sz1, power const * pws1, unsigned sz2, power const * pws2, tmp_monomial & r) {
994 if (STORE_RESULT)
995 r.reserve(sz1); // r has at most sz1 arguments.
996 unsigned i1 = 0;
997 unsigned i2 = 0;
998 unsigned j = 0;
999 if (sz1 < sz2)
1000 return false; // pws2 does not divide pws1
1001 while (true) {
1002 if (i2 == sz2) {
1003 if (STORE_RESULT) {
1004 for (; i1 < sz1; i1++, j++)
1005 r.set_power(j, pws1[i1]);
1006 r.set_size(j);
1007 }
1008 return true;
1009 }
1010 if (i1 == sz1)
1011 return false; // pws2 does not divide pws1
1012 power const & pw1 = pws1[i1];
1013 power const & pw2 = pws2[i2];
1014 unsigned v1 = pw1.get_var();
1015 unsigned v2 = pw2.get_var();
1016 if (v1 == v2) {
1017 unsigned d1 = pw1.degree();
1018 unsigned d2 = pw2.degree();
1019 if (d1 < d2)
1020 return false; // pws2 does not divide pws1
1021 if (STORE_RESULT) {
1022 if (d1 > d2) {
1023 r.set_power(j, power(v1, d1 - d2));
1024 j++;
1025 }
1026 }
1027 i1++;
1028 i2++;
1029 }
1030 else if (v1 < v2) {
1031 if (STORE_RESULT) {
1032 r.set_power(j, pw1);
1033 j++;
1034 }
1035 i1++;
1036 }
1037 else {
1038 SASSERT(v1 > v2);
1039 return false; // pws2 does not divide pws1
1040 }
1041 }
1042 }
1043
div(monomial const * m1,monomial const * m2)1044 bool div(monomial const * m1, monomial const * m2) {
1045 if (m1->total_degree() < m2->total_degree())
1046 return false;
1047 if (m1 == m2)
1048 return true;
1049 return div_core<false>(m1->size(), m1->get_powers(), m2->size(), m2->get_powers(), m_tmp1);
1050 }
1051
div(monomial const * m1,monomial const * m2,monomial_ref & r)1052 bool div(monomial const * m1, monomial const * m2, monomial_ref & r) {
1053 if (m1->total_degree() < m2->total_degree())
1054 return false;
1055 if (m1 == m2) {
1056 r = m_unit;
1057 return true;
1058 }
1059 tmp_monomial & div_tmp = m_tmp1;
1060 if (div_core<true>(m1->size(), m1->get_powers(), m2->size(), m2->get_powers(), div_tmp)) {
1061 r = mk_monomial(div_tmp);
1062 return true;
1063 }
1064 return false;
1065 }
1066
1067 /**
1068 \brief Compute the gcd of pws1 and pws2, store it in g, and pws1/g in r1, and pws2/g in r2
1069 Return true if the gcd is not 1. If the result is false, then g, r1 and r2 should not be used.
1070 */
gcd_core(unsigned sz1,power const * pws1,unsigned sz2,power const * pws2,tmp_monomial & g,tmp_monomial & r1,tmp_monomial & r2)1071 bool gcd_core(unsigned sz1, power const * pws1, unsigned sz2, power const * pws2, tmp_monomial & g, tmp_monomial & r1, tmp_monomial & r2) {
1072 g.reserve(std::min(sz1, sz2));
1073 r1.reserve(sz2); // r1 has at most num_args2 arguments
1074 r2.reserve(sz1); // r2 has at most num_args1 arguments
1075 bool found = false;
1076 unsigned i1 = 0;
1077 unsigned i2 = 0;
1078 unsigned j1 = 0;
1079 unsigned j2 = 0;
1080 unsigned j3 = 0;
1081 while (true) {
1082 if (i1 == sz1) {
1083 if (found) {
1084 for (; i2 < sz2; i2++, j2++)
1085 r2.set_power(j2, pws2[i2]);
1086 r1.set_size(j1);
1087 r2.set_size(j2);
1088 g.set_size(j3);
1089 return true;
1090 }
1091 return false;
1092 }
1093 if (i2 == sz2) {
1094 if (found) {
1095 for (; i1 < sz1; i1++, j1++)
1096 r1.set_power(j1, pws1[i1]);
1097 r1.set_size(j1);
1098 r2.set_size(j2);
1099 g.set_size(j3);
1100 return true;
1101 }
1102 return false;
1103 }
1104 power const & pw1 = pws1[i1];
1105 power const & pw2 = pws2[i2];
1106 unsigned v1 = pw1.get_var();
1107 unsigned v2 = pw2.get_var();
1108 if (v1 == v2) {
1109 found = true;
1110 unsigned d1 = pw1.degree();
1111 unsigned d2 = pw2.degree();
1112 if (d1 > d2) {
1113 r1.set_power(j1, power(v1, d1 - d2));
1114 g.set_power(j3, pw2);
1115 j1++;
1116 j3++;
1117 }
1118 else if (d2 > d1) {
1119 r2.set_power(j2, power(v2, d2 - d1));
1120 g.set_power(j3, pw1);
1121 j2++;
1122 j3++;
1123 }
1124 else {
1125 SASSERT(d1 == d2);
1126 g.set_power(j3, pw1);
1127 j3++;
1128 }
1129 i1++;
1130 i2++;
1131 }
1132 else if (v1 < v2) {
1133 r1.set_power(j1, pw1);
1134 j1++;
1135 i1++;
1136 }
1137 else {
1138 SASSERT(v1 > v2);
1139 r2.set_power(j2, pw2);
1140 j2++;
1141 i2++;
1142 }
1143 }
1144 }
1145
gcd(monomial const * m1,monomial const * m2,monomial * & q1,monomial * & q2)1146 monomial * gcd(monomial const * m1, monomial const * m2, monomial * & q1, monomial * & q2) {
1147 if (gcd_core(m1->size(), m1->get_powers(), m2->size(), m2->get_powers(), m_tmp1, m_tmp2, m_tmp3)) {
1148 q1 = mk_monomial(m_tmp2);
1149 q2 = mk_monomial(m_tmp3);
1150 return mk_monomial(m_tmp1);
1151 }
1152 else {
1153 // gcd is one
1154 q1 = const_cast<monomial*>(m2);
1155 q2 = const_cast<monomial*>(m1);
1156 return m_unit;
1157 }
1158 }
1159
unify(monomial const * m1,monomial const * m2,monomial * & q1,monomial * & q2)1160 bool unify(monomial const * m1, monomial const * m2, monomial * & q1, monomial * & q2) {
1161 if (gcd_core(m1->size(), m1->get_powers(), m2->size(), m2->get_powers(), m_tmp1, m_tmp2, m_tmp3)) {
1162 q1 = mk_monomial(m_tmp2);
1163 q2 = mk_monomial(m_tmp3);
1164 return true;
1165 }
1166 return false;
1167 }
1168
pw(monomial const * m,unsigned k)1169 monomial * pw(monomial const * m, unsigned k) {
1170 if (k == 0)
1171 return m_unit;
1172 if (k == 1)
1173 return const_cast<monomial*>(m);
1174 unsigned sz = m->size();
1175 tmp_monomial & pw_tmp = m_tmp1;
1176 pw_tmp.reserve(sz);
1177 for (unsigned i = 0; i < sz; i++)
1178 pw_tmp.set_power(i, power(m->get_var(i), m->degree(i)*k));
1179 pw_tmp.set_size(sz);
1180 return mk_monomial(pw_tmp);
1181 }
1182
sqrt(monomial const * m)1183 monomial * sqrt(monomial const * m) {
1184 SASSERT(m_unit != 0);
1185 if (m == m_unit)
1186 return m_unit;
1187 unsigned sz = m->size();
1188 tmp_monomial & sqrt_tmp = m_tmp1;
1189 sqrt_tmp.reserve(sz);
1190 for (unsigned i = 0; i < sz; i++) {
1191 if (m->degree(i) % 2 == 1)
1192 return nullptr;
1193 sqrt_tmp.set_power(i, power(m->get_var(i), m->degree(i) / 2));
1194 }
1195 sqrt_tmp.set_size(sz);
1196 return mk_monomial(sqrt_tmp);
1197 }
1198
1199 /**
1200 \brief Return m/x^k
1201 */
div_x_k(monomial const * m,var x,unsigned k)1202 monomial * div_x_k(monomial const * m, var x, unsigned k) {
1203 SASSERT(is_valid(x));
1204 unsigned sz = m->size();
1205 tmp_monomial & elim_tmp = m_tmp1;
1206 elim_tmp.reserve(sz);
1207 unsigned j = 0;
1208 for (unsigned i = 0; i < sz; i++) {
1209 power const & pw = m->get_power(i);
1210 var y = pw.get_var();
1211 if (x != y) {
1212 elim_tmp.set_power(j, pw);
1213 j++;
1214 }
1215 else {
1216 SASSERT(k <= pw.degree());
1217 unsigned d = pw.degree();
1218 if (k < d) {
1219 elim_tmp.set_power(j, power(y, d - k));
1220 j++;
1221 }
1222 }
1223 }
1224 elim_tmp.set_size(j);
1225 return mk_monomial(elim_tmp);
1226 }
1227
1228 /**
1229 \brief Return m/x^n where n == m->degree_of(x)
1230 */
div_x(monomial const * m,var x)1231 monomial * div_x(monomial const * m, var x) {
1232 SASSERT(is_valid(x));
1233 unsigned sz = m->size();
1234 tmp_monomial & elim_tmp = m_tmp1;
1235 elim_tmp.reserve(sz);
1236 unsigned j = 0;
1237 for (unsigned i = 0; i < sz; i++) {
1238 power const & pw = m->get_power(i);
1239 var y = pw.get_var();
1240 if (x != y) {
1241 elim_tmp.set_power(j, pw);
1242 j++;
1243 }
1244 }
1245 elim_tmp.set_size(j);
1246 return mk_monomial(elim_tmp);
1247 }
1248
derivative(monomial const * m,var x)1249 monomial * derivative(monomial const * m, var x) {
1250 SASSERT(is_valid(x));
1251 unsigned sz = m->size();
1252 tmp_monomial & derivative_tmp = m_tmp1;
1253 derivative_tmp.reserve(sz);
1254 unsigned j = 0;
1255 for (unsigned i = 0; i < sz; i++) {
1256 power const & pw = m->get_power(i);
1257 var y = pw.get_var();
1258 if (x == y) {
1259 unsigned d = pw.degree();
1260 if (d > 1) {
1261 derivative_tmp.set_power(j, power(y, d-1));
1262 j++;
1263 }
1264 }
1265 else {
1266 derivative_tmp.set_power(j, pw);
1267 j++;
1268 }
1269 }
1270 derivative_tmp.set_size(j);
1271 return mk_monomial(derivative_tmp);
1272 }
1273
rename(unsigned sz,var const * xs)1274 void rename(unsigned sz, var const * xs) {
1275 SASSERT(m_ref_count <= 1);
1276 SASSERT(sz == num_vars());
1277 DEBUG_CODE({
1278 // check whether xs is really a permutation
1279 bool_vector found;
1280 found.resize(num_vars(), false);
1281 for (unsigned i = 0; i < sz; i++) {
1282 SASSERT(xs[i] < num_vars());
1283 SASSERT(!found[xs[i]]);
1284 found[xs[i]] = true;
1285 }
1286 });
1287 monomial_table new_table;
1288 monomial_table::iterator it = m_monomials.begin();
1289 monomial_table::iterator end = m_monomials.end();
1290 for (; it != end; ++it) {
1291 monomial * m = *it;
1292 m->rename(sz, xs);
1293 SASSERT(!new_table.contains(m));
1294 new_table.insert(m);
1295 }
1296 m_monomials.swap(new_table);
1297 }
1298
1299 };
1300
1301
1302 /**
1303 We maintain the following invariant:
1304 The first monomial m of every non-zero polynomial p contains:
1305 1) the maximal variable x of p,
1306 2) and the degree of x in m is maximal in p.
1307 */
1308 class polynomial {
1309 public:
1310 typedef manager::numeral numeral;
1311 private:
1312 unsigned m_ref_count;
1313 unsigned m_id:31;
1314 unsigned m_lex_sorted:1;
1315 unsigned m_size;
1316 numeral * m_as;
1317 monomial ** m_ms;
1318
lex_sort(unsigned start,unsigned end,var x,vector<unsigned_vector> & buckets,unsigned_vector & p)1319 void lex_sort(unsigned start, unsigned end, var x, vector<unsigned_vector> & buckets, unsigned_vector & p) {
1320 SASSERT(end > start);
1321 unsigned max_degree = 0;
1322 for (unsigned i = start, j = 0; i < end; i++, j++) {
1323 monomial * m = m_ms[i];
1324 unsigned d = m->degree_of(x);
1325 buckets.reserve(d+1);
1326 buckets[d].push_back(j);
1327 if (d > max_degree)
1328 max_degree = d;
1329 }
1330 p.reset();
1331 unsigned i = max_degree + 1;
1332 while (i > 0) {
1333 --i;
1334 p.append(buckets[i]);
1335 buckets[i].reset();
1336 }
1337 SASSERT(p.size() == end - start);
1338 apply_permutation(p.size(), m_as + start, p.data());
1339 apply_permutation_core(p.size(), m_ms + start, p.data()); // p is not needed anymore after this command
1340 i = start;
1341 while (i < end) {
1342 monomial * m = m_ms[i];
1343 unsigned d = m->degree_of(x);
1344 if (d == 0) {
1345 // x does not occur in m
1346 // since we sorted, x should not in the rest
1347 // we should find the maximal variable variable smaller than x in [i, end)
1348 var y = max_smaller_than(i, end, x);
1349 if (y != null_var)
1350 lex_sort(i, end, y, buckets, p);
1351 return;
1352 }
1353 unsigned j = i + 1;
1354 for (; j < end; j++) {
1355 unsigned d_j = m_ms[j]->degree_of(x);
1356 SASSERT(d_j <= d); // it is sorted
1357 if (d_j < d)
1358 break;
1359 }
1360 SASSERT(j == end || m_ms[j]->degree_of(x) < d);
1361 // sort interval [i, j) using the maximal variable y smaller than x
1362 if (j > i + 1) {
1363 // only need to sort if the interval has more than one element.
1364 var y = max_smaller_than(i, j, x);
1365 if (y != null_var)
1366 lex_sort(i, j, y, buckets, p);
1367 }
1368 i = j;
1369 }
1370 }
1371
1372 public:
ref_count() const1373 unsigned ref_count() const { return m_ref_count; }
inc_ref()1374 void inc_ref() { m_ref_count++; }
dec_ref()1375 void dec_ref() { SASSERT(m_ref_count > 0); m_ref_count--; }
1376
get_obj_size(unsigned n)1377 static unsigned get_obj_size(unsigned n) { return sizeof(polynomial) + n * (sizeof(numeral) + sizeof(monomial*)); }
1378
1379 /**
1380 \brief Partial order used to implement the polynomial invariant that guarantees
1381 that the first monomial contains the maximal variable in the polynomial, and it
1382 occurs with maximal degree.
1383
1384 Return true if m1 > m2 in this partial order.
1385 */
po_gt(monomial const * m1,monomial const * m2)1386 static bool po_gt(monomial const * m1, monomial const * m2) {
1387 if (m1->size() == 0)
1388 return false;
1389 if (m2->size() == 0)
1390 return true;
1391 if (m1->max_var() < m2->max_var())
1392 return false;
1393 if (m1->max_var() > m2->max_var())
1394 return true;
1395 SASSERT(m1->max_var() == m2->max_var());
1396 return m1->max_var_degree() > m2->max_var_degree();
1397 }
1398
1399 // swap monomials at positions 0 and pos
swap_0_pos(unsigned pos)1400 void swap_0_pos(unsigned pos) {
1401 if (pos != 0) {
1402 swap(m_as[0], m_as[pos]);
1403 std::swap(m_ms[0], m_ms[pos]);
1404 }
1405 }
1406
polynomial(mpzzp_manager & nm,unsigned id,unsigned sz,numeral * as,monomial * const * ms,numeral * as_mem,monomial ** ms_mem)1407 polynomial(mpzzp_manager & nm, unsigned id, unsigned sz, numeral * as, monomial * const * ms, numeral * as_mem, monomial ** ms_mem):
1408 m_ref_count(0),
1409 m_id(id),
1410 m_lex_sorted(false),
1411 m_size(sz),
1412 m_as(as_mem),
1413 m_ms(ms_mem) {
1414 if (sz > 0) {
1415 unsigned max_pos = 0;
1416 for (unsigned i = 0; i < sz; i++) {
1417 new (m_as + i) numeral(); // initialize the big number at m_as[i]
1418 swap(m_as[i], as[i]);
1419 SASSERT(ms[i]->ref_count() > 0);
1420 m_ms[i] = ms[i];
1421 if (i > 0 && po_gt(m_ms[i], m_ms[max_pos]))
1422 max_pos = i;
1423 }
1424 swap_0_pos(max_pos);
1425 }
1426 }
1427
1428 // Return the maximal variable y occurring in [m_ms + start, m_ms + end) that is smaller than x
max_smaller_than(unsigned start,unsigned end,var x)1429 var max_smaller_than(unsigned start, unsigned end, var x) {
1430 var max = null_var;
1431 for (unsigned i = start; i < end; i++) {
1432 var y = m_ms[i]->max_smaller_than(x);
1433 if (y != null_var && (max == null_var || y > max))
1434 max = y;
1435 }
1436 return max;
1437 }
1438
lex_sorted() const1439 bool lex_sorted() const {
1440 return m_lex_sorted;
1441 }
1442
1443 // Put monomials in lexicographical order
lex_sort(vector<unsigned_vector> & buckets,unsigned_vector & p,mpzzp_manager & nm)1444 void lex_sort(vector<unsigned_vector> & buckets, unsigned_vector & p, mpzzp_manager & nm) {
1445 if (m_lex_sorted)
1446 return;
1447 if (size() <= 1) {
1448 m_lex_sorted = true;
1449 return;
1450 }
1451 lex_sort(0, size(), m(0)->max_var(), buckets, p);
1452 m_lex_sorted = true;
1453 DEBUG_CODE({
1454 for (unsigned i = 0; i < m_size - 1; i++) {
1455 CTRACE("poly_bug", lex_compare(m_ms[i], m_ms[i+1]) <= 0,
1456 tout << "i: " << i << "\npoly: "; display(tout, nm); tout << "\n";);
1457 SASSERT(lex_compare(m_ms[i], m_ms[i+1]) > 0);
1458 }
1459 });
1460 }
1461
1462 /**
1463 \brief Make sure that the first monomial contains the maximal variable x occurring in the polynomial,
1464 and x occurs with maximal degree.
1465 */
make_first_maximal()1466 void make_first_maximal() {
1467 if (m_size <= 1)
1468 return;
1469 unsigned max_pos = 0;
1470 for (unsigned i = 1; i < m_size; i++) {
1471 if (po_gt(m_ms[i], m_ms[max_pos]))
1472 max_pos = i;
1473 }
1474 swap_0_pos(max_pos);
1475 m_lex_sorted = false;
1476 }
1477
1478 /**
1479 \brief Return the position of the maximal monomial with
1480 respect to graded lexicographical order. Return UINT_MAX
1481 if polynomial is zero.
1482 */
graded_lex_max_pos() const1483 unsigned graded_lex_max_pos() const {
1484 if (m_size == 0)
1485 return UINT_MAX;
1486 unsigned max_pos = 0;
1487 for (unsigned i = 1; i < m_size; i++) {
1488 if (graded_lex_compare(m_ms[i], m_ms[max_pos]) > 0)
1489 max_pos = i;
1490 }
1491 return max_pos;
1492 }
1493
1494 /**
1495 \brief Return the position of the minimal monomial with
1496 respect to graded lexicographical order. Return UINT_MAX
1497 if polynomial is zero.
1498 */
graded_lex_min_pos() const1499 unsigned graded_lex_min_pos() const {
1500 if (m_size == 0)
1501 return UINT_MAX;
1502 unsigned min_pos = 0;
1503 for (unsigned i = 1; i < m_size; i++) {
1504 if (graded_lex_compare(m_ms[i], m_ms[min_pos]) < 0)
1505 min_pos = i;
1506 }
1507 return min_pos;
1508 }
1509
id() const1510 unsigned id() const { return m_id; }
size() const1511 unsigned size() const { return m_size; }
m(unsigned idx) const1512 monomial * m(unsigned idx) const { SASSERT(idx < size()); return m_ms[idx]; }
begin() const1513 monomial *const* begin() const { return m_ms; }
end() const1514 monomial *const* end() const { return m_ms + size(); }
a(unsigned idx) const1515 numeral const & a(unsigned idx) const { SASSERT(idx < size()); return m_as[idx]; }
a(unsigned idx)1516 numeral & a(unsigned idx) { SASSERT(idx < size()); return m_as[idx]; }
as() const1517 numeral const * as() const { return m_as; }
1518
is_zero() const1519 bool is_zero() const { return m_size == 0; }
1520
display(std::ostream & out,mpzzp_manager & nm,display_var_proc const & proc=display_var_proc (),bool use_star=false) const1521 std::ostream& display(std::ostream & out, mpzzp_manager & nm, display_var_proc const & proc = display_var_proc(), bool use_star = false) const {
1522 if (is_zero()) {
1523 out << "0";
1524 return out;
1525 }
1526
1527 for (unsigned i = 0; i < m_size; i++) {
1528 numeral const & a_i = a(i);
1529 _scoped_numeral<mpzzp_manager> abs_a_i(nm);
1530 nm.set(abs_a_i, a_i);
1531 nm.abs(abs_a_i);
1532
1533 numeral const & a_prime = abs_a_i;
1534 if (i > 0) {
1535 if (nm.is_neg(a_i))
1536 out << " - ";
1537 else
1538 out << " + ";
1539 }
1540 else {
1541 if (nm.is_neg(a_i))
1542 out << "- ";
1543 }
1544
1545 if (m(i)->is_unit()) {
1546 out << nm.to_string(a_prime);
1547 }
1548 else if (nm.is_one(a_prime)) {
1549 m(i)->display(out, proc, use_star);
1550 }
1551 else {
1552 out << nm.to_string(a_prime);
1553 if (use_star)
1554 out << "*";
1555 else
1556 out << " ";
1557 m(i)->display(out, proc, use_star);
1558 }
1559 }
1560 return out;
1561 }
1562
display_num_smt2(std::ostream & out,mpzzp_manager & nm,numeral const & a)1563 static void display_num_smt2(std::ostream & out, mpzzp_manager & nm, numeral const & a) {
1564 if (nm.is_neg(a)) {
1565 out << "(- ";
1566 _scoped_numeral<mpzzp_manager> abs_a(nm);
1567 nm.set(abs_a, a);
1568 nm.neg(abs_a);
1569 nm.display(out, abs_a);
1570 out << ")";
1571 }
1572 else {
1573 nm.display(out, a);
1574 }
1575 }
1576
display_mon_smt2(std::ostream & out,mpzzp_manager & nm,display_var_proc const & proc,unsigned i) const1577 void display_mon_smt2(std::ostream & out, mpzzp_manager & nm, display_var_proc const & proc, unsigned i) const {
1578 SASSERT(i < m_size);
1579 monomial const * m_i = m(i);
1580 numeral const & a_i = a(i);
1581 if (m_i->size() == 0) {
1582 display_num_smt2(out, nm, a_i);
1583 }
1584 else if (nm.is_one(a_i)) {
1585 if (m_i->size() == 1) {
1586 m_i->display_smt2(out, proc);
1587 }
1588 else {
1589 out << "(* ";
1590 m_i->display_smt2(out, proc);
1591 out << ")";
1592 }
1593 }
1594 else {
1595 out << "(* ";
1596 display_num_smt2(out, nm, a_i);
1597 out << " ";
1598 m_i->display_smt2(out, proc);
1599 out << ")";
1600 }
1601 }
1602
display_smt2(std::ostream & out,mpzzp_manager & nm,display_var_proc const & proc=display_var_proc ()) const1603 void display_smt2(std::ostream & out, mpzzp_manager & nm, display_var_proc const & proc = display_var_proc()) const {
1604 if (m_size == 0) {
1605 out << "0";
1606 }
1607 else if (m_size == 1) {
1608 display_mon_smt2(out, nm, proc, 0);
1609 }
1610 else {
1611 out << "(+";
1612 for (unsigned i = 0; i < m_size; i++) {
1613 out << " ";
1614 display_mon_smt2(out, nm, proc, i);
1615 }
1616 out << ")";
1617 }
1618 }
1619
display(std::ostream & out,mpzzp_manager & nm,bool use_star) const1620 void display(std::ostream & out, mpzzp_manager & nm, bool use_star) const {
1621 display(out, nm, display_var_proc(), use_star);
1622 }
1623
1624 };
1625
factors(manager & _m)1626 manager::factors::factors(manager & _m):m_manager(_m), m_total_factors(0) {
1627 m().m().set(m_constant, 1);
1628 }
1629
~factors()1630 manager::factors::~factors() {
1631 reset();
1632 m().m().del(m_constant);
1633 }
1634
reset()1635 void manager::factors::reset() {
1636 for (unsigned i = 0; i < m_factors.size(); ++ i) {
1637 m().dec_ref(m_factors[i]);
1638 }
1639 m_factors.reset();
1640 m_degrees.reset();
1641 m_total_factors = 0;
1642 m().m().set(m_constant, 1);
1643 }
1644
push_back(polynomial * p,unsigned degree)1645 void manager::factors::push_back(polynomial * p, unsigned degree) {
1646 SASSERT(p != 0 && degree > 0);
1647 m_factors.push_back(p);
1648 m_degrees.push_back(degree);
1649 m_total_factors += degree;
1650 m().inc_ref(p);
1651 }
1652
multiply(polynomial_ref & out) const1653 void manager::factors::multiply(polynomial_ref & out) const {
1654 if (m_factors.empty()) {
1655 out = m().mk_const(m_constant);
1656 }
1657 else {
1658 // multiply the factors
1659 for (unsigned i = 0; i < m_factors.size(); ++ i) {
1660 polynomial_ref current(m_factors[i], m());
1661 if (m_degrees[i] > 1) {
1662 m().pw(current, m_degrees[i], current);
1663 }
1664 if (i == 0) {
1665 out = current;
1666 } else {
1667 out = m().mul(out, current);
1668 }
1669 }
1670 // multiply the constant
1671 out = m().mul(m_constant, out);
1672 }
1673 }
1674
display(std::ostream & out) const1675 void manager::factors::display(std::ostream & out) const {
1676 out << m().m().to_string(get_constant());
1677 for (unsigned i = 0; i < m_factors.size(); ++ i) {
1678 out << " * (";
1679 m_manager.display(out, m_factors[i]);
1680 out << ")^" << m_degrees[i];
1681 }
1682 }
1683
set_constant(numeral const & constant)1684 void manager::factors::set_constant(numeral const & constant) {
1685 m().m().set(m_constant, constant);
1686 }
1687
set_degree(unsigned i,unsigned degree)1688 void manager::factors::set_degree(unsigned i, unsigned degree) {
1689 SASSERT(i > 0);
1690 m_total_factors -= m_degrees[i];
1691 m_total_factors += m_degrees[i] = degree;
1692 }
1693
operator [](unsigned i) const1694 polynomial_ref manager::factors::operator[](unsigned i) const {
1695 return polynomial_ref(m_factors[i], m());
1696 }
1697
id(monomial const * m)1698 unsigned manager::id(monomial const * m) {
1699 return m->id();
1700 }
1701
id(polynomial const * p)1702 unsigned manager::id(polynomial const * p) {
1703 return p->id();
1704 }
1705
is_unit(monomial const * m)1706 bool manager::is_unit(monomial const * m) {
1707 return m->size() == 0;
1708 }
1709
is_zero(polynomial const * p)1710 bool manager::is_zero(polynomial const * p) {
1711 return p->size() == 0;
1712 }
1713
is_const(polynomial const * p)1714 bool manager::is_const(polynomial const * p) {
1715 return is_zero(p) || (p->size() == 1 && is_unit(p->m(0)));
1716 }
1717
is_univariate(monomial const * m)1718 bool manager::is_univariate(monomial const * m) {
1719 return m->size() <= 1;
1720 }
1721
is_univariate(polynomial const * p)1722 bool manager::is_univariate(polynomial const * p) {
1723 unsigned sz = p->size();
1724 if (is_const(p))
1725 return true;
1726 monomial * m = p->m(0);
1727 var x = max_var(p);
1728 for (unsigned i = 0; i < sz; i++) {
1729 m = p->m(i);
1730 if (m->size() == 1 && m->get_var(0) == x)
1731 continue;
1732 if (m->size() == 0)
1733 continue;
1734 return false;
1735 }
1736 return true;
1737 }
1738
size(polynomial const * p)1739 unsigned manager::size(polynomial const * p) {
1740 return p->size();
1741 }
1742
coeff(polynomial const * p,unsigned i)1743 polynomial::numeral const & manager::coeff(polynomial const * p, unsigned i) {
1744 return p->a(i);
1745 }
1746
univ_coeff(polynomial const * p,unsigned k)1747 polynomial::numeral const & manager::univ_coeff(polynomial const * p, unsigned k) {
1748 static numeral zero(0);
1749 SASSERT(is_univariate(p));
1750 unsigned sz = p->size();
1751 for (unsigned i = 0; i < sz; i++) {
1752 if (p->m(i)->total_degree() == k)
1753 return p->a(i);
1754 }
1755 return zero;
1756 }
1757
get_monomial(polynomial const * p,unsigned i)1758 monomial * manager::get_monomial(polynomial const * p, unsigned i) {
1759 return p->m(i);
1760 }
1761
total_degree(monomial const * m)1762 unsigned manager::total_degree(monomial const * m) {
1763 return m->total_degree();
1764 }
1765
size(monomial const * m)1766 unsigned manager::size(monomial const * m) {
1767 return m->size();
1768 }
1769
get_var(monomial const * m,unsigned i)1770 var manager::get_var(monomial const * m, unsigned i) {
1771 return m->get_var(i);
1772 }
1773
degree(monomial const * m,unsigned i)1774 unsigned manager::degree(monomial const * m, unsigned i) {
1775 return m->degree(i);
1776 }
1777
degree_of(monomial const * m,var x)1778 unsigned manager::degree_of(monomial const * m, var x) {
1779 return m->degree_of(x);
1780 }
1781
is_linear(monomial const * m)1782 bool manager::is_linear(monomial const * m) {
1783 return m->size() == 0 || (m->size() == 1 && m->degree(0) == 1);
1784 }
1785
is_linear(polynomial const * p)1786 bool manager::is_linear(polynomial const * p) {
1787 for (monomial* m : *p)
1788 if (!is_linear(m))
1789 return false;
1790 return true;
1791 }
1792
degree(polynomial const * p,var x)1793 unsigned manager::degree(polynomial const * p, var x) {
1794 unsigned sz = p->size();
1795 if (sz == 0)
1796 return 0;
1797 monomial * m = p->m(0);
1798 unsigned msz = m->size();
1799 if (msz == 0)
1800 return 0; // see polynomial invariant.
1801 if (m->get_var(msz - 1) == x) {
1802 // x is the maximal variable in p
1803 return m->degree(msz - 1);
1804 }
1805 unsigned r = 0;
1806 // use slow (linear) scan.
1807 for (unsigned i = 0; i < sz; i++) {
1808 unsigned d = p->m(i)->degree_of(x);
1809 if (d > r)
1810 r = d;
1811 }
1812 return r;
1813 }
1814
max_var(polynomial const * p)1815 var manager::max_var(polynomial const * p) {
1816 if (p->size() == 0)
1817 return null_var;
1818 monomial * m = p->m(0);
1819 return m->max_var();
1820 }
1821
total_degree(polynomial const * p)1822 unsigned manager::total_degree(polynomial const * p) {
1823 // use linear scan... if it turns out to be too slow, I should cache total_degree in polynomial
1824 unsigned r = 0;
1825 unsigned sz = p->size();
1826 for (unsigned i = 0; i < sz; i++) {
1827 unsigned t = p->m(i)->total_degree();
1828 if (t > r)
1829 r = t;
1830 }
1831 return r;
1832 }
1833
1834 struct manager::imp {
1835 typedef upolynomial::manager up_manager;
1836 typedef mpzzp_manager numeral_manager; // refine numeral_manager
1837
1838 typedef _scoped_numeral<numeral_manager> scoped_numeral;
1839 typedef _scoped_numeral_vector<numeral_manager> scoped_numeral_vector;
1840
1841 reslimit& m_limit;
1842 manager & m_wrapper;
1843 numeral_manager m_manager;
1844 up_manager m_upm;
1845 monomial_manager * m_monomial_manager;
1846 polynomial_vector m_polynomials;
1847 id_gen m_pid_gen; // id generator for polynomials
1848 del_eh * m_del_eh;
1849 polynomial * m_zero;
1850 numeral m_zero_numeral;
1851 polynomial * m_unit_poly;
1852 monomial2pos m_m2pos;
1853 tmp_monomial m_tmp1;
1854 numeral_vector m_rat2numeral;
1855 numeral_vector m_tmp_linear_as;
1856 monomial_vector m_tmp_linear_ms;
1857 unsigned_vector m_degree2pos;
1858 bool m_use_sparse_gcd;
1859 bool m_use_prs_gcd;
1860
1861 // Debugging method: check if the coefficients of p are in the numeral_manager.
consistent_coeffspolynomial::manager::imp1862 bool consistent_coeffs(polynomial const * p) {
1863 scoped_numeral a(m_manager);
1864 unsigned sz = p->size();
1865 for (unsigned i = 0; i < sz; i++) {
1866 m_manager.set(a, p->a(i));
1867 SASSERT(m_manager.eq(a, p->a(i)));
1868 }
1869 return true;
1870 }
1871
1872 /**
1873 \brief Divide as by the GCD of as.
1874 Return true, if the GCD is not 1.
1875 */
normalize_numeralspolynomial::manager::imp1876 static bool normalize_numerals(numeral_manager & m, numeral_vector & as) {
1877 unsigned sz = as.size();
1878 if (sz == 0)
1879 return false;
1880 scoped_numeral g(m);
1881 m.gcd(as.size(), as.data(), g);
1882 if (m.is_one(g))
1883 return false;
1884 SASSERT(m.is_pos(g));
1885 for (unsigned i = 0; i < sz; i++) {
1886 m.div(as[i], g, as[i]);
1887 }
1888 return true;
1889 }
1890
1891 /**
1892 \brief Som-of-monomials buffer.
1893 This a temporary datastructure for building polynomials.
1894
1895 The following idiom should be used:
1896 Invoke add(...), addmul(...) several times, and then invoke mk() to obtain the final polynomial.
1897 */
1898 class som_buffer {
1899 imp * m_owner;
1900 monomial2pos m_m2pos;
1901 numeral_vector m_tmp_as;
1902 monomial_vector m_tmp_ms;
1903
1904 /**
1905 \brief Remove zeros from m_tmp_as & m_tmp_ms.
1906 The reference counters of eliminated m_tmp_ms are decremented.
1907 m_m2pos is reset. That is for every m in m_tmp_ms, m_m2pos[m->id()] == UINT_MAX
1908 */
remove_zeros(bool normalize)1909 void remove_zeros(bool normalize) {
1910 numeral_manager & mng = m_owner->m_manager;
1911 SASSERT(m_tmp_ms.size() == m_tmp_as.size());
1912 unsigned sz = m_tmp_ms.size();
1913 unsigned j = 0;
1914 for (unsigned i = 0; i < sz; i++) {
1915 monomial * m = m_tmp_ms[i];
1916 m_m2pos.reset(m);
1917 if (mng.is_zero(m_tmp_as[i])) {
1918 mng.reset(m_tmp_as[i]);
1919 m_owner->dec_ref(m_tmp_ms[i]);
1920 }
1921 else {
1922 if (i != j) {
1923 SASSERT(m_tmp_ms[j] != m);
1924 m_tmp_ms[j] = m;
1925 swap(m_tmp_as[j], m_tmp_as[i]);
1926 }
1927 j++;
1928 }
1929 }
1930 DEBUG_CODE({
1931 for (unsigned i = j; i < sz; i++) {
1932 SASSERT(mng.is_zero(m_tmp_as[i]));
1933 }
1934 });
1935 m_tmp_as.shrink(j);
1936 m_tmp_ms.shrink(j);
1937 if (normalize) {
1938 normalize_numerals(mng, m_tmp_as);
1939 }
1940 }
1941
1942 public:
som_buffer()1943 som_buffer():m_owner(nullptr) {}
1944
reset()1945 void reset() {
1946 if (empty())
1947 return;
1948 numeral_manager & mng = m_owner->m_manager;
1949 SASSERT(m_tmp_ms.size() == m_tmp_as.size());
1950 unsigned sz = m_tmp_ms.size();
1951 for (unsigned i = 0; i < sz; i++) {
1952 monomial * m = m_tmp_ms[i];
1953 m_m2pos.reset(m);
1954 mng.reset(m_tmp_as[i]);
1955 m_owner->dec_ref(m_tmp_ms[i]);
1956 }
1957 m_tmp_as.reset();
1958 m_tmp_ms.reset();
1959 }
1960
set_owner(imp * o)1961 void set_owner(imp * o) { m_owner = o; }
1962
size() const1963 unsigned size() const { return m_tmp_ms.size(); }
1964
empty() const1965 bool empty() const { return m_tmp_ms.empty(); }
1966
m(unsigned i) const1967 monomial * m(unsigned i) const { return m_tmp_ms[i]; }
1968
a(unsigned i) const1969 numeral const & a(unsigned i) const { return m_tmp_as[i]; }
1970
1971 /**
1972 \brief Return the position of the maximal monomial with
1973 respect to graded lexicographical order.
1974
1975 Return UINT_MAX if empty.
1976 */
graded_lex_max_pos() const1977 unsigned graded_lex_max_pos() const {
1978 numeral_manager & mng = m_owner->m_manager;
1979 unsigned max_pos = UINT_MAX;
1980 unsigned sz = m_tmp_as.size();
1981 for (unsigned i = 0; i < sz; i++) {
1982 if (!mng.is_zero(m_tmp_as[i])) {
1983 if (max_pos == UINT_MAX) {
1984 max_pos = i;
1985 }
1986 else {
1987 if (graded_lex_compare(m_tmp_ms[i], m_tmp_ms[max_pos]) > 0)
1988 max_pos = i;
1989 }
1990 }
1991 }
1992 return max_pos;
1993 }
1994
1995 /**
1996 \brief Store a*m*p into the buffer.
1997 m_m2pos is updated with the position of the monomials in m_tmp_ms.
1998
1999 The reference counter of new monomials added into the buffer is increased.
2000 */
2001 template<typename PolyType, bool CheckZeros>
addmul_core(numeral const & a,monomial const * m,PolyType const * p)2002 void addmul_core(numeral const & a, monomial const * m, PolyType const * p) {
2003 numeral_manager & mng = m_owner->m_manager;
2004 if (mng.is_zero(a))
2005 return;
2006 unsigned sz = p->size();
2007 for (unsigned i = 0; i < sz; i++) {
2008 if (CheckZeros && mng.is_zero(p->a(i)))
2009 continue;
2010 monomial * m2 = p->m(i);
2011 m2 = m_owner->mul(m, m2);
2012 unsigned pos = m_m2pos.get(m2);
2013 if (pos == UINT_MAX) {
2014 m_m2pos.set(m2, m_tmp_ms.size());
2015 m_tmp_ms.push_back(m2);
2016 m_owner->inc_ref(m2);
2017 m_tmp_as.push_back(numeral());
2018 mng.mul(a, p->a(i), m_tmp_as.back());
2019 }
2020 else {
2021 mng.addmul(m_tmp_as[pos], a, p->a(i), m_tmp_as[pos]);
2022 }
2023 }
2024 }
2025
addmul(numeral const & a,monomial const * m,polynomial const * p)2026 void addmul(numeral const & a, monomial const * m, polynomial const * p) {
2027 return addmul_core<polynomial, false>(a, m, p);
2028 }
2029
addmul(numeral const & a,monomial const * m,som_buffer const * p)2030 void addmul(numeral const & a, monomial const * m, som_buffer const * p) {
2031 return addmul_core<som_buffer, false>(a, m, p);
2032 }
2033
addmul(numeral const & a,monomial const * m,som_buffer const & p)2034 void addmul(numeral const & a, monomial const * m, som_buffer const & p) {
2035 return addmul(a, m, &p);
2036 }
2037
addmul(numeral const & a,som_buffer const * p)2038 void addmul(numeral const & a, som_buffer const * p) {
2039 return addmul(a, m_owner->mk_unit(), p);
2040 }
2041
addmul(numeral const & a,som_buffer const & p)2042 void addmul(numeral const & a, som_buffer const & p) {
2043 return addmul(a, &p);
2044 }
2045
addmul(monomial const * m,som_buffer const * p)2046 void addmul(monomial const * m, som_buffer const * p) {
2047 numeral one(1);
2048 return addmul(one, m, p);
2049 }
2050
addmul(monomial const * m,som_buffer const & p)2051 void addmul(monomial const * m, som_buffer const & p) {
2052 return addmul(m, &p);
2053 }
2054
2055 /**
2056 \brief Store p into the buffer.
2057 m_m2pos is updated with the position of the monomials in m_tmp_ms.
2058
2059 The reference counter of new monomials added into the buffer is increased.
2060 */
add(polynomial const * p)2061 void add(polynomial const * p) {
2062 numeral_manager & mng = m_owner->m_manager;
2063 unsigned sz = p->size();
2064 for (unsigned i = 0; i < sz; i++) {
2065 monomial * m2 = p->m(i);
2066 unsigned pos = m_m2pos.get(m2);
2067 if (pos == UINT_MAX) {
2068 m_m2pos.set(m2, m_tmp_ms.size());
2069 m_tmp_ms.push_back(m2);
2070 m_owner->inc_ref(m2);
2071 m_tmp_as.push_back(numeral());
2072 mng.set(m_tmp_as.back(), p->a(i));
2073 }
2074 else {
2075 mng.add(m_tmp_as[pos], p->a(i), m_tmp_as[pos]);
2076 }
2077 }
2078 }
2079
2080 /**
2081 \brief Add 'a*m' into m_tmp_as and m_tmp_ms.
2082 m_m2pos is updated with the position of the monomials in m_tmp_ms.
2083
2084 The reference counter of m is increased.
2085 */
add(numeral const & a,monomial * m)2086 void add(numeral const & a, monomial * m) {
2087 numeral_manager & mng = m_owner->m_manager;
2088 if (mng.is_zero(a))
2089 return;
2090 unsigned pos = m_m2pos.get(m);
2091 if (pos == UINT_MAX) {
2092 m_m2pos.set(m, m_tmp_ms.size());
2093 m_owner->inc_ref(m);
2094 m_tmp_ms.push_back(m);
2095 m_tmp_as.push_back(numeral());
2096 mng.set(m_tmp_as.back(), a);
2097 }
2098 else {
2099 mng.add(m_tmp_as[pos], a, m_tmp_as[pos]);
2100 }
2101 }
2102
2103 /**
2104 \brief Add 'a' (that is, a*m_unit) into m_tmp_as and m_tmp_ms.
2105 m_m2pos is updated with the position of the monomials in m_tmp_ms.
2106
2107 The reference counter of m_unit is increased.
2108 */
add(numeral const & a)2109 void add(numeral const & a) {
2110 add(a, m_owner->mk_unit());
2111 }
2112
sort_graded_lex()2113 void sort_graded_lex() {
2114 std::sort(m_tmp_ms.begin(), m_tmp_ms.end(), graded_lex_gt());
2115 numeral_vector new_as;
2116 unsigned sz = m_tmp_ms.size();
2117 for (unsigned i = 0; i < sz; i++) {
2118 monomial * m = m_tmp_ms[i];
2119 unsigned pos = m_m2pos.get(m);
2120 new_as.push_back(numeral());
2121 swap(new_as.back(), m_tmp_as[pos]);
2122 m_m2pos.reset(m);
2123 m_m2pos.set(m, i);
2124 }
2125 m_tmp_as.swap(new_as);
2126 }
2127
2128 // For each monomial m
2129 // If m contains x^k and k >= x2d[x] and x2d[x] != 0, then set coefficient of m to 0.
mod_d(var2degree const & x2d)2130 void mod_d(var2degree const & x2d) {
2131 numeral_manager & mng = m_owner->m_manager;
2132 unsigned sz = m_tmp_ms.size();
2133 for (unsigned i = 0; i < sz; i++) {
2134 if (mng.is_zero(m_tmp_as[i]))
2135 continue;
2136 monomial * m = m_tmp_ms[i];
2137 unsigned msz = m->size();
2138 unsigned j;
2139 for (j = 0; j < msz; j++) {
2140 var x = m->get_var(j);
2141 unsigned dx = x2d.degree(x);
2142 if (dx == 0)
2143 continue;
2144 if (m->degree(j) >= dx)
2145 break;
2146 }
2147 if (j < msz) {
2148 mng.reset(m_tmp_as[i]);
2149 }
2150 }
2151 }
2152
mk(bool normalize=false)2153 polynomial * mk(bool normalize = false) {
2154 remove_zeros(normalize);
2155 polynomial * p = m_owner->mk_polynomial_core(m_tmp_as.size(), m_tmp_as.data(), m_tmp_ms.data());
2156 m_tmp_as.reset();
2157 m_tmp_ms.reset();
2158 return p;
2159 }
2160
display(std::ostream & out) const2161 void display(std::ostream & out) const {
2162 SASSERT(m_tmp_ms.size() == m_tmp_as.size());
2163 numeral_manager & mng = m_owner->m_manager;
2164 for (unsigned i = 0; i < m_tmp_as.size(); i++) {
2165 if (i > 0) out << " + ";
2166 out << mng.to_string(m_tmp_as[i]) << "*"; m_tmp_ms[i]->display(out);
2167 }
2168 out << "\n";
2169 }
2170 };
2171
2172 class som_buffer_vector {
2173 imp * m_owner;
2174 ptr_vector<som_buffer> m_buffers;
2175
ensure_capacity(unsigned sz)2176 void ensure_capacity(unsigned sz) {
2177 unsigned old_sz = m_buffers.size();
2178 for (unsigned i = old_sz; i < sz; i++) {
2179 som_buffer * new_buffer = alloc(som_buffer);
2180 if (m_owner)
2181 new_buffer->set_owner(m_owner);
2182 m_buffers.push_back(new_buffer);
2183 }
2184 SASSERT(m_buffers.size() >= sz);
2185 }
2186
2187 public:
som_buffer_vector()2188 som_buffer_vector() {
2189 m_owner = nullptr;
2190 }
2191
~som_buffer_vector()2192 ~som_buffer_vector() {
2193 clear();
2194 }
2195
clear()2196 void clear() {
2197 reset();
2198 unsigned sz = m_buffers.size();
2199 for (unsigned i = 0; i < sz; i++) {
2200 dealloc(m_buffers[i]);
2201 }
2202 m_buffers.reset();
2203 }
2204
set_owner(imp * owner)2205 void set_owner(imp * owner) {
2206 SASSERT(m_owner == owner || m_owner == 0);
2207 if (m_owner == nullptr) {
2208 m_owner = owner;
2209 unsigned sz = m_buffers.size();
2210 for (unsigned i = 0; i < sz; i++) {
2211 m_buffers[i]->set_owner(m_owner);
2212 }
2213 }
2214 }
2215
operator [](unsigned idx)2216 som_buffer * operator[](unsigned idx) {
2217 ensure_capacity(idx+1);
2218 return m_buffers[idx];
2219 }
2220
reset(unsigned sz)2221 void reset(unsigned sz) {
2222 if (sz > m_buffers.size())
2223 sz = m_buffers.size();
2224 for (unsigned i = 0; i < sz; i++) {
2225 m_buffers[i]->reset();
2226 }
2227 }
2228
reset()2229 void reset() {
2230 reset(m_buffers.size());
2231 }
2232
2233 };
2234
2235 /**
2236 \brief Cheap version of som_buffer.
2237 In this buffer, each monomial can be added at most once.
2238 */
2239 class cheap_som_buffer {
2240 imp * m_owner;
2241 numeral_vector m_tmp_as;
2242 monomial_vector m_tmp_ms;
2243 public:
cheap_som_buffer()2244 cheap_som_buffer():m_owner(nullptr) {}
2245
set_owner(imp * o)2246 void set_owner(imp * o) { m_owner = o; }
empty() const2247 bool empty() const { return m_tmp_ms.empty(); }
2248
2249 /**
2250 \brief Add a*m to the buffer, the content of a is reset.
2251 */
add_reset(numeral & a,monomial * m)2252 void add_reset(numeral & a, monomial * m) {
2253 SASSERT(std::find(m_tmp_ms.begin(), m_tmp_ms.end(), m) == m_tmp_ms.end());
2254 numeral_manager & mng = m_owner->m_manager;
2255 if (mng.is_zero(a))
2256 return;
2257 m_tmp_as.push_back(numeral());
2258 swap(m_tmp_as.back(), a);
2259 m_owner->inc_ref(m);
2260 m_tmp_ms.push_back(m);
2261 }
2262
2263 /**
2264 \brief Add a*m to the buffer.
2265 */
add(numeral const & a,monomial * m)2266 void add(numeral const & a, monomial * m) {
2267 SASSERT(std::find(m_tmp_ms.begin(), m_tmp_ms.end(), m) == m_tmp_ms.end());
2268 numeral_manager & mng = m_owner->m_manager;
2269 if (mng.is_zero(a))
2270 return;
2271 m_tmp_as.push_back(numeral());
2272 mng.set(m_tmp_as.back(), a);
2273 m_owner->inc_ref(m);
2274 m_tmp_ms.push_back(m);
2275 }
2276
2277 /**
2278 \brief Add a*m*p to the buffer.
2279 */
addmul(numeral const & a,monomial const * m,polynomial const * p)2280 void addmul(numeral const & a, monomial const * m, polynomial const * p) {
2281 numeral_manager & mng = m_owner->m_manager;
2282 if (mng.is_zero(a))
2283 return;
2284 unsigned sz = p->size();
2285 for (unsigned i = 0; i < sz; i++) {
2286 monomial * m2 = p->m(i);
2287 m2 = m_owner->mul(m, m2);
2288 // m2 is not in m_tmp_ms
2289 SASSERT(std::find(m_tmp_ms.begin(), m_tmp_ms.end(), m2) == m_tmp_ms.end());
2290 m_owner->inc_ref(m2);
2291 m_tmp_ms.push_back(m2);
2292 m_tmp_as.push_back(numeral());
2293 mng.mul(a, p->a(i), m_tmp_as.back());
2294 }
2295 }
2296
normalize()2297 bool normalize() {
2298 return normalize_numerals(m_owner->m_manager, m_tmp_as);
2299 }
2300
reset()2301 void reset() {
2302 if (empty())
2303 return;
2304 numeral_manager & mng = m_owner->m_manager;
2305 unsigned sz = m_tmp_ms.size();
2306 for (unsigned i = 0; i < sz; i++) {
2307 mng.del(m_tmp_as[i]);
2308 m_owner->dec_ref(m_tmp_ms[i]);
2309 }
2310 m_tmp_as.reset();
2311 m_tmp_ms.reset();
2312 }
2313
mk()2314 polynomial * mk() {
2315 polynomial * new_p = m_owner->mk_polynomial_core(m_tmp_as.size(), m_tmp_as.data(), m_tmp_ms.data());
2316 m_tmp_as.reset();
2317 m_tmp_ms.reset();
2318 return new_p;
2319 }
2320 };
2321
2322 som_buffer m_som_buffer;
2323 som_buffer m_som_buffer2;
2324 cheap_som_buffer m_cheap_som_buffer;
2325 cheap_som_buffer m_cheap_som_buffer2;
2326
initpolynomial::manager::imp2327 void init() {
2328 m_del_eh = nullptr;
2329 m_som_buffer.set_owner(this);
2330 m_som_buffer2.set_owner(this);
2331 m_cheap_som_buffer.set_owner(this);
2332 m_cheap_som_buffer2.set_owner(this);
2333 m_zero = mk_polynomial_core(0, nullptr, nullptr);
2334 m().set(m_zero_numeral, 0);
2335 inc_ref(m_zero);
2336 numeral one(1);
2337 m_unit_poly = mk_const_core(one);
2338 inc_ref(m_unit_poly);
2339 m_use_sparse_gcd = true;
2340 m_use_prs_gcd = false;
2341 }
2342
imppolynomial::manager::imp2343 imp(reslimit& lim, manager & w, unsynch_mpz_manager & m, monomial_manager * mm):
2344 m_limit(lim),
2345 m_wrapper(w),
2346 m_manager(m),
2347 m_upm(lim, m) {
2348 if (mm == nullptr)
2349 mm = alloc(monomial_manager);
2350 m_monomial_manager = mm;
2351 m_monomial_manager->inc_ref();
2352 init();
2353 }
2354
imppolynomial::manager::imp2355 imp(reslimit& lim, manager & w, unsynch_mpz_manager & m, small_object_allocator * a):
2356 m_limit(lim),
2357 m_wrapper(w),
2358 m_manager(m),
2359 m_upm(lim, m) {
2360 m_monomial_manager = alloc(monomial_manager, a);
2361 m_monomial_manager->inc_ref();
2362 init();
2363 }
2364
~imppolynomial::manager::imp2365 ~imp() {
2366 dec_ref(m_zero);
2367 dec_ref(m_unit_poly);
2368 m_som_buffer.reset();
2369 m_som_buffer2.reset();
2370 m_cheap_som_buffer.reset();
2371 m_cheap_som_buffer2.reset();
2372 m_manager.del(m_zero_numeral);
2373 m_mgcd_iterpolators.flush();
2374 m_mgcd_skeletons.reset();
2375 CTRACE("polynomial", !m_polynomials.empty(),
2376 tout << "leaked polynomials\n";
2377 for (auto* p : m_polynomials) {
2378 if (p) p->display(tout, m_manager) << "\n";
2379 });
2380 m_polynomials.reset();
2381 SASSERT(m_polynomials.empty());
2382 m_iccp_ZpX_buffers.clear();
2383 m_monomial_manager->dec_ref();
2384 }
2385
checkpointpolynomial::manager::imp2386 void checkpoint() {
2387 if (!m_limit.inc()) {
2388 throw polynomial_exception(Z3_CANCELED_MSG);
2389 }
2390 }
2391
mpolynomial::manager::imp2392 mpzzp_manager & m() const { return const_cast<imp*>(this)->m_manager; }
pmpolynomial::manager::imp2393 manager & pm() const { return m_wrapper; }
upmpolynomial::manager::imp2394 up_manager & upm() { return m_upm; }
mmpolynomial::manager::imp2395 monomial_manager & mm() const { return *m_monomial_manager; }
2396
mk_varpolynomial::manager::imp2397 var mk_var() {
2398 return mm().mk_var();
2399 }
2400
num_varspolynomial::manager::imp2401 unsigned num_vars() const {
2402 return mm().num_vars();
2403 }
2404
is_validpolynomial::manager::imp2405 bool is_valid(var x) const {
2406 return mm().is_valid(x);
2407 }
2408
2409
add_del_ehpolynomial::manager::imp2410 void add_del_eh(del_eh * eh) {
2411 eh->m_next = m_del_eh;
2412 m_del_eh = eh;
2413 }
2414
remove_del_ehpolynomial::manager::imp2415 void remove_del_eh(del_eh * eh) {
2416 SASSERT(eh != 0);
2417 SASSERT(m_del_eh != 0);
2418 if (m_del_eh == eh) {
2419 m_del_eh = m_del_eh->m_next;
2420 }
2421 else {
2422 del_eh * curr = m_del_eh;
2423 while (curr) {
2424 if (curr->m_next == eh) {
2425 curr->m_next = curr->m_next->m_next;
2426 return;
2427 }
2428 curr = curr->m_next;
2429 }
2430 UNREACHABLE();
2431 }
2432 }
2433
delpolynomial::manager::imp2434 void del(polynomial * p) {
2435 TRACE("polynomial", tout << "deleting: "; p->display(tout, m_manager); tout << "\n";);
2436 if (m_del_eh != nullptr) {
2437 del_eh * curr = m_del_eh;
2438 do {
2439 (*curr)(p);
2440 curr = curr->m_next;
2441 }
2442 while (curr != nullptr);
2443 }
2444 unsigned sz = p->size();
2445 unsigned obj_sz = polynomial::get_obj_size(sz);
2446 for (unsigned i = 0; i < sz; i++) {
2447 m_manager.del(p->a(i));
2448 dec_ref(p->m(i));
2449 }
2450 unsigned id = p->id();
2451 m_pid_gen.recycle(id);
2452 m_polynomials[id] = 0;
2453 mm().allocator().deallocate(obj_sz, p);
2454 }
2455
inc_refpolynomial::manager::imp2456 void inc_ref(monomial * m) {
2457 mm().inc_ref(m);
2458 }
2459
dec_refpolynomial::manager::imp2460 void dec_ref(monomial * m) {
2461 mm().dec_ref(m);
2462 }
2463
inc_refpolynomial::manager::imp2464 void inc_ref(polynomial * p) {
2465 p->inc_ref();
2466 }
2467
dec_refpolynomial::manager::imp2468 void dec_ref(polynomial * p) {
2469 p->dec_ref();
2470 if (p->ref_count() == 0)
2471 del(p);
2472 }
2473
2474 vector<unsigned_vector> m_lex_sort_buckets;
2475 unsigned_vector m_lex_sort_permutation;
lex_sortpolynomial::manager::imp2476 void lex_sort(polynomial const * p) {
2477 const_cast<polynomial*>(p)->lex_sort(m_lex_sort_buckets, m_lex_sort_permutation, m_manager);
2478 }
2479
operator ()polynomial::manager::imp::poly_khasher2480 struct poly_khasher { unsigned operator()(polynomial const * p) const { return 17; } };
2481
2482 struct poly_chasher {
operator ()polynomial::manager::imp::poly_chasher2483 unsigned operator()(polynomial const * p, unsigned idx) const {
2484 return hash_u_u(p->m(idx)->hash(), numeral_manager::hash(p->a(idx)));
2485 }
2486 };
2487
hashpolynomial::manager::imp2488 unsigned hash(polynomial const * p) {
2489 if (p->size() == 0)
2490 return 31;
2491 lex_sort(const_cast<polynomial*>(p));
2492 return get_composite_hash(p, p->size(), poly_khasher(), poly_chasher());
2493 }
2494
mk_polynomial_corepolynomial::manager::imp2495 polynomial * mk_polynomial_core(unsigned sz, numeral * as, monomial * const * ms) {
2496 unsigned obj_sz = polynomial::get_obj_size(sz);
2497 void * mem = mm().allocator().allocate(obj_sz);
2498 void * as_mem = static_cast<char*>(mem) + sizeof(polynomial);
2499 void * ms_mem = static_cast<char*>(as_mem) + sizeof(numeral)*sz;
2500 unsigned id = m_pid_gen.mk();
2501 polynomial * p = new (mem) polynomial(m_manager, id, sz, as, ms, static_cast<numeral*>(as_mem), static_cast<monomial**>(ms_mem));
2502 m_polynomials.reserve(id+1);
2503 SASSERT(m_polynomials[id] == 0);
2504 m_polynomials[id] = p;
2505 return p;
2506 }
2507
gcd_simplifypolynomial::manager::imp2508 void gcd_simplify(polynomial * p) {
2509 if (m_manager.finite()) return;
2510 auto& m = m_manager.m();
2511 unsigned sz = p->size();
2512 if (sz == 0)
2513 return;
2514 unsigned g = 0;
2515 for (unsigned i = 0; i < sz; i++) {
2516 if (!m.is_int(p->a(i))) {
2517 return;
2518 }
2519 int j = m.get_int(p->a(i));
2520 if (j == INT_MIN || j == 1 || j == -1)
2521 return;
2522 g = u_gcd(abs(j), g);
2523 if (g == 1)
2524 return;
2525 }
2526 scoped_mpz r(m), gg(m);
2527 m.set(gg, g);
2528 for (unsigned i = 0; i < sz; ++i) {
2529 m.div_gcd(p->a(i), gg, r);
2530 m.set(p->a(i), r);
2531 }
2532 }
2533
mk_zeropolynomial::manager::imp2534 polynomial * mk_zero() {
2535 return m_zero;
2536 }
2537
mk_onepolynomial::manager::imp2538 polynomial * mk_one() {
2539 return m_unit_poly;
2540 }
2541
mk_unitpolynomial::manager::imp2542 monomial * mk_unit() { return mm().mk_unit(); }
2543
mk_monomialpolynomial::manager::imp2544 monomial * mk_monomial(tmp_monomial & tmp) { return mm().mk_monomial(tmp); }
2545
mk_monomialpolynomial::manager::imp2546 monomial * mk_monomial(var x) { return mm().mk_monomial(x); }
2547
mk_monomialpolynomial::manager::imp2548 monomial * mk_monomial(var x, unsigned k) { return mm().mk_monomial(x, k); }
2549
mk_monomialpolynomial::manager::imp2550 monomial * mk_monomial(unsigned sz, var * xs) { return mm().mk_monomial(sz, xs); }
2551
mk_monomialpolynomial::manager::imp2552 monomial * mk_monomial(unsigned sz, power const * pws) { return mm().mk_monomial(sz, pws); }
2553
convertpolynomial::manager::imp2554 monomial * convert(monomial const * src) { return mm().convert(src); }
2555
mk_const_corepolynomial::manager::imp2556 polynomial * mk_const_core(numeral & a) {
2557 monomial * u = mk_unit();
2558 inc_ref(u);
2559 return mk_polynomial_core(1, &a, &u);
2560 }
2561
mk_constpolynomial::manager::imp2562 polynomial * mk_const(numeral & a) {
2563 if (m_manager.is_zero(a))
2564 return mk_zero();
2565 if (m_manager.is_one(a))
2566 return mk_one();
2567 return mk_const_core(a);
2568 }
2569
mk_constpolynomial::manager::imp2570 polynomial * mk_const(rational const & a) {
2571 SASSERT(a.is_int());
2572 scoped_numeral tmp(m_manager);
2573 m_manager.set(tmp, a.to_mpq().numerator());
2574 polynomial * p = mk_const(tmp);
2575 return p;
2576 }
2577
mk_polynomialpolynomial::manager::imp2578 polynomial * mk_polynomial(var x, unsigned k) {
2579 SASSERT(is_valid(x));
2580 numeral one(1);
2581 monomial * m = mk_monomial(x, k);
2582 inc_ref(m);
2583 return mk_polynomial_core(1, &one, &m);
2584 }
2585
mk_polynomialpolynomial::manager::imp2586 polynomial * mk_polynomial(unsigned sz, numeral * as, monomial * const * ms) {
2587 m_som_buffer.reset();
2588 for (unsigned i = 0; i < sz; i++) {
2589 m_som_buffer.add(as[i], ms[i]);
2590 }
2591 return m_som_buffer.mk();
2592 }
2593
2594 /**
2595 \brief Convert rationals into numerals at m_rat2numeral
2596 */
rational2numeralpolynomial::manager::imp2597 void rational2numeral(unsigned sz, rational const * as) {
2598 SASSERT(m_rat2numeral.empty());
2599 for (unsigned i = 0; i < sz; i++) {
2600 SASSERT(as[i].is_int());
2601 m_rat2numeral.push_back(numeral());
2602 m_manager.set(m_rat2numeral.back(), as[i].to_mpq().numerator());
2603 }
2604 }
2605
reset_tmp_as2polynomial::manager::imp2606 void reset_tmp_as2() {
2607 DEBUG_CODE({
2608 for (unsigned i = 0; i < m_rat2numeral.size(); i++) {
2609 SASSERT(m_manager.is_zero(m_rat2numeral[i]));
2610 }
2611 });
2612 m_rat2numeral.reset();
2613 }
2614
mk_polynomialpolynomial::manager::imp2615 polynomial * mk_polynomial(unsigned sz, rational const * as, monomial * const * ms) {
2616 rational2numeral(sz, as);
2617 polynomial * p = mk_polynomial(sz, m_rat2numeral.data(), ms);
2618 reset_tmp_as2();
2619 return p;
2620 }
2621
mk_univariatepolynomial::manager::imp2622 polynomial * mk_univariate(var x, unsigned n, numeral * as) {
2623 SASSERT(m_cheap_som_buffer.empty());
2624 unsigned k = n+1;
2625 while (k > 0) {
2626 --k;
2627 if (m_manager.is_zero(as[k])) {
2628 m_manager.del(as[k]);
2629 continue;
2630 }
2631 m_cheap_som_buffer.add_reset(as[k], mk_monomial(x, k));
2632 }
2633 return m_cheap_som_buffer.mk();
2634 }
2635
mk_univariatepolynomial::manager::imp2636 polynomial * mk_univariate(var x, unsigned n, rational const * as) {
2637 SASSERT(is_valid(x));
2638 rational2numeral(n+1, as);
2639 polynomial * p = mk_univariate(x, n, m_rat2numeral.data());
2640 reset_tmp_as2();
2641 return p;
2642 }
2643
mk_linearpolynomial::manager::imp2644 polynomial * mk_linear(unsigned sz, numeral * as, var const * xs, numeral & c) {
2645 SASSERT(m_tmp_linear_as.empty());
2646 SASSERT(m_tmp_linear_ms.empty());
2647 for (unsigned i = 0; i < sz; i++) {
2648 if (m_manager.is_zero(as[i]))
2649 continue;
2650 m_tmp_linear_as.push_back(numeral());
2651 swap(m_tmp_linear_as.back(), as[i]);
2652 m_tmp_linear_ms.push_back(mk_monomial(xs[i]));
2653 }
2654 if (!m_manager.is_zero(c)) {
2655 m_tmp_linear_as.push_back(numeral());
2656 swap(m_tmp_linear_as.back(), c);
2657 m_tmp_linear_ms.push_back(mk_unit());
2658 }
2659 polynomial * p = mk_polynomial(m_tmp_linear_as.size(), m_tmp_linear_as.data(), m_tmp_linear_ms.data());
2660 for (auto& a : m_tmp_linear_as) {
2661 m_manager.del(a);
2662 }
2663 m_tmp_linear_as.reset();
2664 m_tmp_linear_ms.reset();
2665 return p;
2666 }
2667
mk_linearpolynomial::manager::imp2668 polynomial * mk_linear(unsigned sz, rational const * as, var const * xs, rational const & c) {
2669 SASSERT(c.is_int());
2670 rational2numeral(sz, as);
2671 numeral tmp_c;
2672 m_manager.set(tmp_c, c.to_mpq().numerator());
2673 polynomial * p = mk_linear(sz, m_rat2numeral.data(), xs, tmp_c);
2674 SASSERT(m_manager.is_zero(tmp_c));
2675 reset_tmp_as2();
2676 return p;
2677 }
2678
2679
mulpolynomial::manager::imp2680 monomial * mul(monomial const * m1, monomial const * m2) {
2681 return mm().mul(m1, m2);
2682 }
2683
divpolynomial::manager::imp2684 bool div(monomial const * m1, monomial const * m2) {
2685 return mm().div(m1, m2);
2686 }
2687
divpolynomial::manager::imp2688 bool div(monomial const * m1, monomial const * m2, monomial_ref & r) {
2689 return mm().div(m1, m2, r);
2690 }
2691
gcdpolynomial::manager::imp2692 monomial * gcd(monomial const * m1, monomial const * m2, monomial * & q1, monomial * & q2) {
2693 return mm().gcd(m1, m2, q1, q2);
2694 }
2695
unifypolynomial::manager::imp2696 bool unify(monomial const * m1, monomial const * m2, monomial * & q1, monomial * & q2) {
2697 return mm().unify(m1, m2, q1, q2);
2698 }
2699
pwpolynomial::manager::imp2700 monomial * pw(monomial const * m, unsigned k) {
2701 return mm().pw(m, k);
2702 }
2703
sqrtpolynomial::manager::imp2704 monomial * sqrt(monomial const * m) {
2705 return mm().sqrt(m);
2706 }
2707
addmulpolynomial::manager::imp2708 polynomial * addmul(numeral const & a1, monomial const * m1, polynomial const * p1, numeral const & a2, monomial const * m2, polynomial const * p2) {
2709 m_som_buffer.reset();
2710 m_som_buffer.addmul(a1, m1, p1);
2711 m_som_buffer.addmul(a2, m2, p2);
2712 return m_som_buffer.mk();
2713 }
2714
addmulpolynomial::manager::imp2715 polynomial * addmul(polynomial const * p1, numeral const & a2, monomial const * m2, polynomial const * p2) {
2716 numeral one(1);
2717 return addmul(one, mk_unit(), p1, a2, m2, p2);
2718 }
2719
addmulpolynomial::manager::imp2720 polynomial * addmul(polynomial const * p1, numeral const & a2, polynomial const * p2) {
2721 return addmul(p1, a2, mk_unit(), p2);
2722 }
2723
addpolynomial::manager::imp2724 polynomial * add(polynomial const * p1, polynomial const * p2) {
2725 numeral one(1);
2726 return addmul(one, mk_unit(), p1, one, mk_unit(), p2);
2727 }
2728
subpolynomial::manager::imp2729 polynomial * sub(polynomial const * p1, polynomial const * p2) {
2730 numeral one(1);
2731 numeral minus_one; // It is incorrect to initialize with -1 when numeral_manager is GF_2
2732 m_manager.set(minus_one, -1);
2733 return addmul(one, mk_unit(), p1, minus_one, mk_unit(), p2);
2734 }
2735
2736 /**
2737 \brief Return p1*p2 + a
2738 */
muladdpolynomial::manager::imp2739 polynomial * muladd(polynomial const * p1, polynomial const * p2, numeral const & a) {
2740 if (is_zero(p1) || is_zero(p2)) {
2741 return mk_const(a);
2742 }
2743 m_som_buffer.reset();
2744 unsigned sz1 = p1->size();
2745 for (unsigned i = 0; i < sz1; i++) {
2746 checkpoint();
2747 numeral const & a1 = p1->a(i);
2748 monomial * m1 = p1->m(i);
2749 m_som_buffer.addmul(a1, m1, p2);
2750 }
2751 m_som_buffer.add(a);
2752 return m_som_buffer.mk();
2753 }
2754
mulpolynomial::manager::imp2755 polynomial * mul(polynomial const * p1, polynomial const * p2) {
2756 numeral zero(0);
2757 return muladd(p1, p2, zero);
2758 }
2759
mulpolynomial::manager::imp2760 polynomial * mul(numeral const & a, monomial const * m, polynomial const * p) {
2761 if (m_manager.is_zero(a))
2762 return m_zero;
2763 if (m_manager.is_one(a) && m == mk_unit())
2764 return const_cast<polynomial*>(p);
2765 SASSERT(m_cheap_som_buffer.empty());
2766 m_cheap_som_buffer.addmul(a, m, p);
2767 return m_cheap_som_buffer.mk();
2768 }
2769
mulpolynomial::manager::imp2770 polynomial * mul(monomial const * m, polynomial const * p) {
2771 numeral one(1);
2772 return mul(one, m, p);
2773 }
2774
mulpolynomial::manager::imp2775 polynomial * mul(numeral const & a, polynomial const * p) {
2776 return mul(a, mk_unit(), p);
2777 }
2778
2779 /**
2780 \brief Return a*p1*p2
2781 */
mulpolynomial::manager::imp2782 polynomial * mul(numeral const & a, polynomial const * p1, polynomial const * p2) {
2783 if (m_manager.is_zero(a) || is_zero(p1) || is_zero(p2))
2784 return mk_zero();
2785 scoped_numeral new_a1(m_manager);
2786 m_som_buffer.reset();
2787 unsigned sz1 = p1->size();
2788 for (unsigned i = 0; i < sz1; i++) {
2789 checkpoint();
2790 numeral const & a1 = p1->a(i);
2791 m_manager.mul(a, a1, new_a1);
2792 monomial * m1 = p1->m(i);
2793 m_som_buffer.addmul(new_a1, m1, p2);
2794 }
2795 return m_som_buffer.mk();
2796 }
2797
2798 // Divide coefficients of p by d.
2799 // This methods assumes that all coefficients of p are divisible by d.
divpolynomial::manager::imp2800 polynomial * div(polynomial * p, numeral const & d) {
2801 SASSERT(m_cheap_som_buffer.empty());
2802 unsigned sz = p->size();
2803 scoped_numeral a(m_manager);
2804 for (unsigned i = 0; i < sz; i++) {
2805 m_manager.div(p->a(i), d, a);
2806 m_cheap_som_buffer.add(a, p->m(i));
2807 }
2808 return m_cheap_som_buffer.mk();
2809 }
2810
mulpolynomial::manager::imp2811 polynomial * mul(rational const & a, polynomial const * p) {
2812 SASSERT(a.is_int());
2813 scoped_numeral tmp(m_manager);
2814 m_manager.set(tmp, a.to_mpq().numerator());
2815 polynomial * new_p = mul(tmp, p);
2816 return new_p;
2817 }
2818
2819 /**
2820 \brief Return m/x^k
2821 */
div_x_kpolynomial::manager::imp2822 monomial * div_x_k(monomial const * m, var x, unsigned k) {
2823 return mm().div_x_k(m, x, k);
2824 }
2825
2826 /**
2827 \brief Return m/x^n where n == m->degree_of(x)
2828 */
div_xpolynomial::manager::imp2829 monomial * div_x(monomial const * m, var x) {
2830 return mm().div_x(m, x);
2831 }
2832
is_p_normalizedpolynomial::manager::imp2833 bool is_p_normalized(polynomial const * p) const {
2834 for (unsigned i = 0; i < p->size(); i++) {
2835 SASSERT(m().is_p_normalized(p->a(i)));
2836 }
2837 return true;
2838 }
2839
2840 /**
2841 \brief (Incremental) Newton interpolation for multivariate polynomials.
2842 Creates a polynomial on x of degree at most d with coefficients in Z[y1, ..., ym],
2843 using d+1 sample points.
2844 Sample points are provided using the method add, and the interpolating polynomial
2845 is created using mk() method.
2846
2847 \pre manager must be configured in Zp (modular) mode.
2848 We need this requeriment because we use the inverse operation.
2849 */
2850 class newton_interpolator {
2851 imp & pm;
2852 scoped_numeral_vector m_inputs;
2853 scoped_numeral_vector m_invs;
2854 polynomial_ref_vector m_vs;
m() const2855 mpzzp_manager & m() const { return pm.m(); }
2856 public:
newton_interpolator(imp & _pm)2857 newton_interpolator(imp & _pm):pm(_pm), m_inputs(m()), m_invs(m()), m_vs(pm.m_wrapper) {
2858 m_invs.push_back(numeral(0));
2859 }
2860
reset()2861 void reset() {
2862 m_inputs.reset();
2863 m_invs.shrink(1);
2864 m_vs.reset();
2865 SASSERT(m().is_zero(m_invs[0]));
2866 }
2867
inputs() const2868 scoped_numeral_vector const & inputs() const { return m_inputs; }
2869
num_sample_points() const2870 unsigned num_sample_points() const { return m_inputs.size(); }
2871
2872 /**
2873 \brief Add a new datapoint
2874 */
add(numeral const & input,polynomial const * output)2875 void add(numeral const & input, polynomial const * output) {
2876 TRACE("newton", tout << m().to_string(input) << " -> "; output->display(tout, m()); tout << "\n";);
2877 SASSERT(m().modular());
2878 unsigned sz = num_sample_points();
2879 if (sz > 0) {
2880 unsigned k = sz;
2881 // add new inverse
2882 scoped_numeral product(m());
2883 scoped_numeral aux(m());
2884 SASSERT(!m().eq(input, m_inputs[0]));
2885 m().sub(input, m_inputs[0], product);
2886 for (unsigned i = 1; i <= k - 1; i++) {
2887 SASSERT(!m().eq(input, m_inputs[i]));
2888 m().sub(input, m_inputs[i], aux);
2889 m().mul(product, aux, product);
2890 }
2891 m().inv(product);
2892 m_inputs.push_back(input);
2893 m_invs.push_back(product);
2894 TRACE("newton", tout << "invs[" << k << "]: " << product << "\n";);
2895 SASSERT(m().eq(m_inputs[k], input));
2896 // Compute newton's coefficient
2897 polynomial_ref temp(pm.m_wrapper);
2898 polynomial_ref aux_poly(pm.m_wrapper);
2899 temp = m_vs.get(k-1);
2900 for (int j = k - 2; j >= 0; j--) {
2901 // temp <- temp*(input - m_inputs[j]) + vs[j]
2902 m().sub(input, m_inputs[j], aux);
2903 SASSERT(m().is_p_normalized(aux));
2904 aux_poly = pm.mul(aux, temp);
2905 temp = pm.add(aux_poly, m_vs.get(j));
2906 SASSERT(pm.is_p_normalized(temp));
2907 }
2908 // new vs <- (output - temp)*invs[sz]
2909 aux_poly = pm.sub(output, temp);
2910 SASSERT(pm.is_p_normalized(aux_poly));
2911 aux_poly = pm.mul(m_invs[sz], aux_poly);
2912 SASSERT(pm.is_p_normalized(aux_poly));
2913 m_vs.push_back(aux_poly);
2914 TRACE("newton", tout << "vs[" << k << "]: " << aux_poly << "\n";);
2915 }
2916 else {
2917 m_inputs.push_back(input);
2918 m_vs.push_back(const_cast<polynomial*>(output));
2919 }
2920 }
2921
2922 // Convert newton form to standard form
mk(var x,polynomial_ref & r)2923 void mk(var x, polynomial_ref & r) {
2924 SASSERT(m().modular());
2925 polynomial_ref u(pm.m_wrapper);
2926 polynomial_ref aux_poly(pm.m_wrapper);
2927 int num = num_sample_points();
2928 int d = num - 1;
2929 SASSERT(num > 0);
2930 u = m_vs.get(d);
2931 scoped_numeral c(m());
2932 for (int k = d - 1; k >= 0; k--) {
2933 TRACE("newton", tout << "u: " << u << "\n";);
2934 // create polynomial (x - inputs[k])
2935 m().set(c, m_inputs[k]);
2936 m().neg(c);
2937 numeral one(1);
2938 aux_poly = pm.mk_linear(1, &one, &x, c);
2939 TRACE("newton", tout << "(x - inputs[k]): " << aux_poly << "\n";);
2940 // u <- u * (x - inputs[k]) + vs[k]
2941 aux_poly = pm.mul(u, aux_poly);
2942 u = pm.add(aux_poly, m_vs.get(k));
2943 }
2944 TRACE("newton", tout << "result u: " << u << "\n";);
2945 r = u;
2946 }
2947 };
2948
2949 /**
2950 \brief Newton interpolation for multivariate polynomials.
2951 Creates a polynomial on x of degree at most d with coefficients in Z[y1, ..., ym],
2952 using d+1 sample points.
2953 The sample points are store in the vectors inputs and outputs. Both must have size d+1.
2954
2955 \pre manager must be configured in Zp (modular) mode.
2956 We need this requeriment because we use the inverse operation.
2957 */
newton_interpolationpolynomial::manager::imp2958 void newton_interpolation(var x, unsigned d, numeral const * inputs, polynomial * const * outputs, polynomial_ref & r) {
2959 SASSERT(m().modular());
2960 newton_interpolator interpolator(*this);
2961 for (unsigned i = 0; i <= d; i++)
2962 interpolator.add(inputs[i], outputs[i]);
2963 interpolator.mk(x, r);
2964 }
2965
2966 class newton_interpolator_vector {
2967 imp * m_imp;
2968 ptr_vector<newton_interpolator> m_data;
2969 public:
newton_interpolator_vector()2970 newton_interpolator_vector():m_imp(nullptr) {}
2971
~newton_interpolator_vector()2972 ~newton_interpolator_vector() {
2973 flush();
2974 }
2975
flush()2976 void flush() {
2977 unsigned sz = m_data.size();
2978 for (unsigned i = 0; i < sz; i++)
2979 dealloc(m_data[i]);
2980 m_data.reset();
2981 }
2982
set_owner(imp * owner)2983 void set_owner(imp * owner) {
2984 SASSERT(m_imp == 0 || m_imp == owner);
2985 m_imp = owner;
2986 }
2987
operator [](unsigned idx)2988 newton_interpolator & operator[](unsigned idx) {
2989 SASSERT(m_imp);
2990 while (idx >= m_data.size()) {
2991 m_data.push_back(alloc(newton_interpolator, *m_imp));
2992 }
2993 return *(m_data[idx]);
2994 }
2995 };
2996
2997 /**
2998 \brief Represents a polynomial skeleton of a multivariate polynomial Z[Y1, ..., Yn]
2999 with coefficients in Z[X]
3000 */
3001 struct skeleton {
3002 struct entry {
3003 monomial * m_monomial; // a monomial in Z[Y1, ..., Y1]
3004 unsigned m_first_power_idx; // position (in m_powers) of the powers of X that are the coefficient of this monomial
3005 unsigned m_num_powers; // size of the coefficient of this monomial.
entrypolynomial::manager::imp::skeleton::entry3006 entry(monomial * m, unsigned first_idx):
3007 m_monomial(m),
3008 m_first_power_idx(first_idx),
3009 m_num_powers(1) {
3010 }
num_powerspolynomial::manager::imp::skeleton::entry3011 unsigned num_powers() const { return m_num_powers; }
mpolynomial::manager::imp::skeleton::entry3012 monomial * m() const { return m_monomial; }
3013 };
3014 imp & pm;
3015 var m_x;
3016 svector<entry> m_entries;
3017 unsigned_vector m_powers;
3018 ptr_vector<monomial> m_orig_monomials;
3019 unsigned m_max_powers; // maximal number of powers associated with an entry
3020
skeletonpolynomial::manager::imp::skeleton3021 skeleton(imp & _pm, polynomial * p, var x):pm(_pm), m_x(x) {
3022 m_max_powers = 0;
3023 ptr_buffer<monomial, 128> ms;
3024 unsigned sz = p->size();
3025 for (unsigned i = 0; i < sz; i++) {
3026 ms.push_back(p->m(i));
3027 }
3028 std::sort(ms.begin(), ms.end(), lex_lt2(x));
3029 monomial * prev = nullptr;
3030 for (unsigned i = 0; i < sz; i++) {
3031 monomial * orig_m = ms[i];
3032 monomial * m;
3033 unsigned k = orig_m->degree_of(x);
3034 if (k > 0)
3035 m = pm.div_x(orig_m, x);
3036 else
3037 m = orig_m;
3038 if (m == prev) {
3039 unsigned & num_powers = m_entries.back().m_num_powers;
3040 num_powers++;
3041 if (num_powers > m_max_powers)
3042 m_max_powers = num_powers;
3043 }
3044 else {
3045 prev = m;
3046 pm.inc_ref(m);
3047 m_entries.push_back(entry(m, m_powers.size()));
3048 if (m_max_powers == 0)
3049 m_max_powers = 1;
3050 }
3051 pm.inc_ref(orig_m);
3052 m_orig_monomials.push_back(orig_m);
3053 m_powers.push_back(k);
3054 }
3055 TRACE("skeleton",
3056 tout << "x: x" << m_x << "\n";
3057 tout << "max: " << m_max_powers << "\n";
3058 tout << "p: "; p->display(tout, pm.m()); tout << "\n";
3059 tout << "skeleton: "; display(tout); tout << "\n";);
3060 DEBUG_CODE({
3061 unsigned sz = m_entries.size();
3062 for (unsigned i = 1; i < sz; i++) {
3063 SASSERT(lex_compare(m_entries[i-1].m_monomial, m_entries[i].m_monomial) < 0);
3064 }
3065 });
3066 }
3067
~skeletonpolynomial::manager::imp::skeleton3068 ~skeleton() {
3069 unsigned sz = m_entries.size();
3070 for (unsigned i = 0; i < sz; i++) {
3071 pm.dec_ref(m_entries[i].m_monomial);
3072 }
3073 sz = m_orig_monomials.size();
3074 for (unsigned i = 0; i < sz; i++) {
3075 pm.dec_ref(m_orig_monomials[i]);
3076 }
3077 }
3078
get_entry_idxpolynomial::manager::imp::skeleton3079 unsigned get_entry_idx(monomial * m) {
3080 unsigned sz = m_entries.size();
3081 for (unsigned i = 0; i < sz; i++) {
3082 if (m_entries[i].m_monomial == m)
3083 return i;
3084 }
3085 return UINT_MAX;
3086 }
3087
num_entriespolynomial::manager::imp::skeleton3088 unsigned num_entries() const { return m_entries.size(); }
3089
operator []polynomial::manager::imp::skeleton3090 entry const & operator[](unsigned idx) const { return m_entries[idx]; }
3091
ith_powerpolynomial::manager::imp::skeleton3092 unsigned ith_power(entry const & e, unsigned i) const { SASSERT(i < e.m_num_powers); return m_powers[e.m_first_power_idx + i]; }
3093
ith_orig_monomialpolynomial::manager::imp::skeleton3094 monomial * ith_orig_monomial(entry const & e, unsigned i) const { SASSERT(i < e.m_num_powers); return m_orig_monomials[e.m_first_power_idx + i]; }
3095
max_num_powerspolynomial::manager::imp::skeleton3096 unsigned max_num_powers() const { return m_max_powers; }
3097
displaypolynomial::manager::imp::skeleton3098 void display(std::ostream & out) {
3099 unsigned sz = m_entries.size();
3100 for (unsigned i = 0; i < sz; i++) {
3101 entry & e = m_entries[i];
3102 if (i > 0) out << " ";
3103 out << "(";
3104 for (unsigned j = 0; j < e.m_num_powers; j++) {
3105 if (j > 0) out << " ";
3106 out << "x" << m_x << "^";
3107 out << m_powers[e.m_first_power_idx + j];
3108 }
3109 out << ")*";
3110 e.m_monomial->display(out);
3111 }
3112 }
3113 };
3114
3115 class sparse_interpolator {
3116 skeleton * m_skeleton;
3117 numeral_vector m_inputs;
3118 numeral_vector m_outputs;
3119 public:
sparse_interpolator(skeleton * sk)3120 sparse_interpolator(skeleton * sk):m_skeleton(sk) {
3121 // reserve space output values associated with each entry
3122 if (sk) {
3123 unsigned sz = sk->num_entries();
3124 for (unsigned i = 0; i < sz; i++) {
3125 unsigned num_powers = (*sk)[i].num_powers();
3126 for (unsigned j = 0; j < num_powers; j++) {
3127 m_outputs.push_back(numeral());
3128 }
3129 }
3130 }
3131 }
3132
~sparse_interpolator()3133 ~sparse_interpolator() {
3134 if (m_skeleton) {
3135 numeral_manager & m = m_skeleton->pm.m();
3136 for (unsigned i = 0; i < m_inputs.size(); i++)
3137 m.del(m_inputs[i]);
3138 for (unsigned i = 0; i < m_outputs.size(); i++)
3139 m.del(m_outputs[i]);
3140 }
3141 }
3142
reset()3143 void reset() {
3144 numeral_manager & m = m_skeleton->pm.m();
3145 for (unsigned i = 0; i < m_inputs.size(); i++) {
3146 m.del(m_inputs[i]);
3147 }
3148 m_inputs.reset();
3149 }
3150
ready() const3151 bool ready() const {
3152 return m_inputs.size() == m_skeleton->max_num_powers();
3153 }
3154
add(numeral const & in,polynomial const * q)3155 bool add(numeral const & in, polynomial const * q) {
3156 SASSERT(m_skeleton);
3157 SASSERT(m_inputs.size() < m_skeleton->max_num_powers());
3158 numeral_manager & m = m_skeleton->pm.m();
3159 unsigned input_idx = m_inputs.size();
3160 m_inputs.push_back(numeral());
3161 m.set(m_inputs.back(), in);
3162 unsigned sz = q->size();
3163 for (unsigned i = 0; i < sz; i++) {
3164 monomial * mon = q->m(i);
3165 unsigned entry_idx = m_skeleton->get_entry_idx(mon);
3166 if (entry_idx == UINT_MAX)
3167 return false;
3168 skeleton::entry const & e = (*m_skeleton)[entry_idx];
3169 if (input_idx >= e.num_powers())
3170 continue;
3171 unsigned output_idx = e.m_first_power_idx + input_idx;
3172 m.set(m_outputs[output_idx], q->a(i));
3173 }
3174 return true;
3175 }
3176
mk(polynomial_ref & r)3177 bool mk(polynomial_ref & r) {
3178 SASSERT(m_skeleton);
3179 numeral_manager & m = m_skeleton->pm.m();
3180 scoped_numeral_vector cs(m);
3181 scoped_numeral_vector new_as(m);
3182 scoped_numeral_vector as(m);
3183 ptr_buffer<monomial,128> mons;
3184 scoped_numeral aux(m);
3185 linear_eq_solver<mpzzp_manager> solver(m);
3186 unsigned sz = m_skeleton->num_entries();
3187 for (unsigned k = 0; k < sz; k++) {
3188 skeleton::entry const & e = (*m_skeleton)[k];
3189 unsigned num_pws = e.num_powers();
3190 solver.resize(num_pws);
3191 new_as.resize(num_pws);
3192 for (unsigned i = 0; i < num_pws; i++) {
3193 numeral & in = m_inputs[i];
3194 cs.reset();
3195 for (unsigned j = 0; j < num_pws; j++) {
3196 m.power(in, m_skeleton->ith_power(e, j), aux);
3197 cs.push_back(aux);
3198 }
3199 unsigned output_idx = e.m_first_power_idx + i;
3200 TRACE("sparse_interpolator", tout << "adding new equation:\n";
3201 for (unsigned i = 0; i < num_pws; i++) {
3202 tout << m.to_string(cs[i]) << " ";
3203 }
3204 tout << "\n";);
3205 solver.add(i, cs.data(), m_outputs[output_idx]);
3206 }
3207 TRACE("sparse_interpolator",
3208 tout << "find coefficients of:\n";
3209 for (unsigned i = 0; i < num_pws; i++) {
3210 m_skeleton->ith_orig_monomial(e, i)->display(tout); tout << "\n";
3211 }
3212 tout << "system of equations:\n";
3213 solver.display(tout););
3214 if (!solver.solve(new_as.data()))
3215 return false;
3216 for (unsigned i = 0; i < num_pws; i++) {
3217 if (!m.is_zero(new_as[i])) {
3218 as.push_back(new_as[i]);
3219 mons.push_back(m_skeleton->ith_orig_monomial(e, i));
3220 }
3221 }
3222 }
3223 r = m_skeleton->pm.mk_polynomial(as.size(), as.data(), mons.data());
3224 return true;
3225 }
3226 };
3227
3228 bool_vector m_found_vars;
varspolynomial::manager::imp3229 void vars(polynomial const * p, var_vector & xs) {
3230 xs.reset();
3231 m_found_vars.reserve(num_vars(), false);
3232 unsigned sz = p->size();
3233 for (unsigned i = 0; i < sz; i++) {
3234 monomial * m = p->m(i);
3235 unsigned msz = m->size();
3236 for (unsigned j = 0; j < msz; j++) {
3237 var x = m->get_var(j);
3238 if (!m_found_vars[x]) {
3239 m_found_vars[x] = true;
3240 xs.push_back(x);
3241 }
3242 }
3243 }
3244 // reset m_found_vars
3245 sz = xs.size();
3246 for (unsigned i = 0; i < sz; i++)
3247 m_found_vars[xs[i]] = false;
3248 }
3249
3250 typedef sbuffer<power, 32> power_buffer;
3251 typedef sbuffer<unsigned, 32> unsigned_buffer;
3252 typedef sbuffer<var, 32> var_buffer;
3253
3254 /**
3255 Store in pws the variables occurring in p and their (minimal or maximal) degrees.
3256 */
3257 unsigned_vector m_var_degrees_tmp;
3258 template<bool Max>
var_degreespolynomial::manager::imp3259 void var_degrees(polynomial const * p, power_buffer & pws) {
3260 pws.reset();
3261 unsigned_vector & var2pos = m_var_degrees_tmp;
3262 var2pos.reserve(num_vars(), UINT_MAX);
3263
3264 unsigned sz = p->size();
3265 for (unsigned i = 0; i < sz; i++) {
3266 monomial * m = p->m(i);
3267 unsigned msz = m->size();
3268 for (unsigned j = 0; j < msz; j++) {
3269 var x = m->get_var(j);
3270 unsigned k = m->degree(j);
3271 unsigned pos = var2pos[x];
3272 if (pos == UINT_MAX) {
3273 var2pos[x] = pws.size();
3274 pws.push_back(power(x, k));
3275 }
3276 else if (Max && k > pws[pos].degree()) {
3277 pws[pos].degree() = k;
3278 }
3279 else if (!Max && k < pws[pos].degree()) {
3280 pws[pos].degree() = k;
3281 }
3282 }
3283 }
3284
3285 sz = pws.size();
3286 for (unsigned i = 0; i < sz; i++) {
3287 SASSERT(var2pos[pws[i].get_var()] != UINT_MAX);
3288 var2pos[pws[i].get_var()] = UINT_MAX;
3289 }
3290
3291 DEBUG_CODE({
3292 for (unsigned i = 0; i < pws.size(); i++) {
3293 for (unsigned j = i + 1; j < pws.size(); j++)
3294 SASSERT(pws[i].first != pws[j].first);
3295 }
3296 });
3297 }
3298
var_max_degreespolynomial::manager::imp3299 void var_max_degrees(polynomial const * p, power_buffer & pws) {
3300 var_degrees<true>(p, pws);
3301 }
3302
var_min_degreespolynomial::manager::imp3303 void var_min_degrees(polynomial const * p, power_buffer & pws) {
3304 var_degrees<false>(p, pws);
3305 }
3306
coeffpolynomial::manager::imp3307 polynomial * coeff(polynomial const * p, var x, unsigned k) {
3308 SASSERT(is_valid(x));
3309 SASSERT(m_cheap_som_buffer.empty());
3310 TRACE("coeff_bug", tout << "p: "; p->display(tout, m_manager); tout << "\nx: " << x << ", k: " << k << "\n";);
3311 unsigned sz = p->size();
3312 for (unsigned i = 0; i < sz; i++) {
3313 monomial * m = p->m(i);
3314 unsigned d = m->degree_of(x);
3315 if (d == k)
3316 m_cheap_som_buffer.add(p->a(i), div_x(m, x));
3317 }
3318 return m_cheap_som_buffer.mk();
3319 }
3320
3321 /**
3322 Let p be of the form q_k(yvec)*x^k + ...+ q_0(yvec)
3323 Store the polynomials q_k(yvec), ..., q_0(yvec) in the som_buffer_vector.
3324 */
coeffspolynomial::manager::imp3325 void coeffs(polynomial const * p, var x, som_buffer_vector & cs) {
3326 cs.set_owner(this);
3327 unsigned sz = p->size();
3328 for (unsigned i = 0; i < sz; i++) {
3329 monomial * m = p->m(i);
3330 unsigned d = m->degree_of(x);
3331 som_buffer * c = cs[d];
3332 c->add(p->a(i), div_x(m, x));
3333 }
3334 }
3335
3336 /**
3337 \brief Return a polynomial h that is the coefficient of x^k in p.
3338 Store the reduct (p - h x^k) into \c reduct.
3339 */
coeffpolynomial::manager::imp3340 polynomial * coeff(polynomial const * p, var x, unsigned k, polynomial_ref & reduct) {
3341 SASSERT(is_valid(x));
3342 SASSERT(m_cheap_som_buffer.empty());
3343 SASSERT(m_cheap_som_buffer2.empty());
3344 unsigned sz = p->size();
3345 for (unsigned i = 0; i < sz; i++) {
3346 monomial * m = p->m(i);
3347 unsigned d = m->degree_of(x);
3348 if (d == k)
3349 m_cheap_som_buffer.add(p->a(i), div_x(m, x));
3350 else
3351 m_cheap_som_buffer2.add(p->a(i), m);
3352 }
3353 reduct = m_cheap_som_buffer2.mk();
3354 return m_cheap_som_buffer.mk();
3355 }
3356
3357 /**
3358 \brief Return true if the coefficient of x^k is just a constant.
3359 Store it in c.
3360 */
const_coeffpolynomial::manager::imp3361 bool const_coeff(polynomial const * p, var x, unsigned k, numeral & c) {
3362 SASSERT(is_valid(x));
3363 m_manager.reset(c);
3364 unsigned sz = p->size();
3365 for (unsigned i = 0; i < sz; i++) {
3366 monomial * m = p->m(i);
3367 unsigned d = m->degree_of(x);
3368 if (d == k) {
3369 unsigned msz = m->size();
3370 if ((k > 0 && msz > 1) || (k == 0 && msz > 0))
3371 return false;
3372 m_manager.set(c, p->a(i));
3373 }
3374 }
3375 return true;
3376 }
3377
nonzero_const_coeffpolynomial::manager::imp3378 bool nonzero_const_coeff(polynomial const * p, var x, unsigned k) {
3379 scoped_numeral c(m_manager);
3380 return const_coeff(p, x, k, c) && !m_manager.is_zero(c);
3381 }
3382
3383 /**
3384 \brief Extract the integer content of p.
3385 */
icpolynomial::manager::imp3386 void ic(polynomial const * p, numeral & a) {
3387 if (is_zero(p)) {
3388 m_manager.reset(a);
3389 return;
3390 }
3391 if (is_const(p)) {
3392 m_manager.set(a, p->a(0));
3393 return;
3394 }
3395 m_manager.set(a, p->a(0));
3396 unsigned sz = p->size();
3397 for (unsigned i = 1; i < sz; i++) {
3398 if (m_manager.is_one(a))
3399 return;
3400 m_manager.gcd(a, p->a(i), a);
3401 }
3402 }
3403
3404 /**
3405 \brief Sum of the absolute values of the coefficients.
3406 */
abs_normpolynomial::manager::imp3407 void abs_norm(polynomial const * p, numeral & norm) {
3408 m_manager.reset(norm);
3409 scoped_numeral tmp(m_manager);
3410 unsigned sz = p->size();
3411 for (unsigned i = 0; i < sz; ++ i) {
3412 m_manager.set(tmp, p->a(i));
3413 m_manager.abs(tmp);
3414 m_manager.add(norm, tmp, norm);
3415 }
3416 }
3417
3418 /**
3419 \brief Arbitrary leading integer coefficient.
3420 */
numeral_lcpolynomial::manager::imp3421 numeral const & numeral_lc(polynomial const * p, var x) {
3422 int sz = p->size();
3423 if (sz == 0) {
3424 return m_zero_numeral;
3425 } else {
3426 return p->a(0);
3427 }
3428 }
3429
numeral_tcpolynomial::manager::imp3430 numeral const & numeral_tc(polynomial const * p) {
3431 int sz = p->size();
3432 if (sz == 0) {
3433 return m_zero_numeral;
3434 }
3435 else {
3436 monomial * u = mk_unit();
3437 for (int i = 0; i < sz; ++ i) {
3438 if (p->m(i) == u)
3439 return p->a(i);
3440 }
3441 return m_zero_numeral;
3442 }
3443 }
3444
3445 /**
3446 \brief Extract the integer content of p.
3447 p = a*c s.t. the GCD of the coefficients of c is one.
3448 */
icpolynomial::manager::imp3449 void ic(polynomial const * p, numeral & a, polynomial_ref & c) {
3450 if (is_zero(p)) {
3451 m_manager.reset(a);
3452 c = const_cast<polynomial*>(p);
3453 return;
3454 }
3455 if (is_const(p)) {
3456 m_manager.set(a, p->a(0));
3457 c = mk_one();
3458 return;
3459 }
3460 unsigned sz = p->size();
3461 m_manager.gcd(sz, p->as(), a);
3462 if (m_manager.is_one(a)) {
3463 c = const_cast<polynomial*>(p);
3464 return;
3465 }
3466 m_cheap_som_buffer.reset();
3467 scoped_numeral ai(m_manager);
3468 for (unsigned i = 0; i < sz; i++) {
3469 monomial * m = p->m(i);
3470 m_manager.div(p->a(i), a, ai);
3471 m_cheap_som_buffer.add_reset(ai, m);
3472 }
3473 c = m_cheap_som_buffer.mk();
3474 }
3475
3476 // Flip the sign of p, if the leading monomial is negative
flip_sign_if_lm_neg_corepolynomial::manager::imp3477 polynomial * flip_sign_if_lm_neg_core(polynomial const * p) {
3478 if (is_zero(p))
3479 return const_cast<polynomial*>(p);
3480 unsigned glex_max_pos = p->graded_lex_max_pos();
3481 SASSERT(glex_max_pos != UINT_MAX);
3482 if (m_manager.is_neg(p->a(glex_max_pos)))
3483 return neg(p);
3484 else
3485 return const_cast<polynomial*>(p);
3486 }
3487
flip_sign_if_lm_negpolynomial::manager::imp3488 void flip_sign_if_lm_neg(polynomial_ref & p) {
3489 p = flip_sign_if_lm_neg_core(p);
3490 }
3491
3492 /**
3493 \brief Extract the integer content, content and primitive part of p with respect to
3494 variable x.
3495 */
iccppolynomial::manager::imp3496 void iccp(polynomial const * p, var x, numeral & i, polynomial_ref & c, polynomial_ref & pp) {
3497 TRACE("polynomial", tout << "iccp x" << x << "\n"; p->display(tout, m_manager); tout << "\n";);
3498 if (is_zero(p)) {
3499 m_manager.set(i, 0);
3500 c = mk_one();
3501 pp = const_cast<polynomial*>(p);
3502 return;
3503 }
3504 if (is_const(p)) {
3505 m_manager.set(i, p->a(0));
3506 c = mk_one();
3507 pp = mk_one();
3508 return;
3509 }
3510 unsigned d = degree(p, x);
3511 if (d == 0) {
3512 ic(p, i, c);
3513 pp = mk_one();
3514 return;
3515 }
3516 // Apply filter and collect powers of x occurring in p
3517 // The quick filter is the following:
3518 // If p contains a monomial x^k and no monomial of the form m*x^k m != 1, then
3519 // c = m_unit_poly
3520 // To detect that we use a map (iccp_powers) from k to counters.
3521 // We traverse p and update the map using the following rules:
3522 // - found monomial x^k then iccp_powers[k]++;
3523 // - found monomial m*x^k then iccp_powers[k]+=2;
3524 // If after traversing p, there is a k s.t. iccp_powers[k] == 1, we know c == 1
3525 // We store iccp_powers the powers of x occurring in p.
3526 sbuffer<unsigned, 128> iccp_filter;
3527 sbuffer<unsigned, 128> iccp_powers;
3528 iccp_filter.resize(d+1, 0);
3529 iccp_powers.reset();
3530 for (unsigned j = 0; j <= d; j++)
3531 iccp_filter[j] = 0;
3532 unsigned sz = p->size();
3533 for (unsigned j = 0; j < sz; j++) {
3534 monomial * m = p->m(j);
3535 unsigned k = m->degree_of(x);
3536 TRACE("polynomial", tout << "degree of x" << x << " at "; m->display(tout); tout << " is " << k << "\n";);
3537 if (iccp_filter[k] == 0)
3538 iccp_powers.push_back(k);
3539 if (m->size() == (k > 0 ? 1 : 0))
3540 iccp_filter[k]++;
3541 else
3542 iccp_filter[k]+=2;
3543 }
3544 SASSERT(!iccp_powers.empty());
3545 unsigned num_powers = iccp_powers.size();
3546 for (unsigned j = 0; j < num_powers; j++) {
3547 SASSERT(iccp_filter[iccp_powers[j]] > 0);
3548 if (iccp_filter[iccp_powers[j]] == 1) {
3549 ic(p, i, pp);
3550 c = mk_one();
3551 return;
3552 }
3553 }
3554 // Extract integer content
3555 ic(p, i, pp);
3556 TRACE("polynomial", tout << "p: "; p->display(tout, m_manager); tout << "\ni: " << m_manager.to_string(i) << "\npp: " << pp << "\n";);
3557 // Compute c using the gcd of coeffs of x^k for k's in iccp_powers
3558 polynomial_ref ci(pm());
3559 c = coeff(pp, x, iccp_powers[0]);
3560 for (unsigned j = 1; j < num_powers; j++) {
3561 ci = coeff(pp, x, iccp_powers[j]);
3562 gcd(c, ci, c);
3563 if (is_const(c)) {
3564 c = mk_one();
3565 return;
3566 }
3567 }
3568 SASSERT(!is_const(c));
3569 // make sure the sign of the leading monomial is positive
3570 flip_sign_if_lm_neg(c);
3571 TRACE("polynomial", tout << "pp: " << pp << "\nc: " << c << "\n";);
3572 pp = exact_div(pp, c);
3573 }
3574
iccppolynomial::manager::imp3575 void iccp(polynomial const * p, numeral & i, polynomial_ref & c, polynomial_ref & pp) {
3576 iccp(p, max_var(p), i, c, pp);
3577 }
3578
pppolynomial::manager::imp3579 polynomial_ref pp(polynomial const * p, var x) {
3580 scoped_numeral i(m_manager);
3581 polynomial_ref c(pm()), result(pm());
3582 iccp(p, x, i, c, result);
3583 return result;
3584 }
3585
is_primitivepolynomial::manager::imp3586 bool is_primitive(polynomial const * p, var x) {
3587 scoped_numeral i(m_manager);
3588 polynomial_ref c(pm());
3589 polynomial_ref pp(pm());
3590 iccp(p, x, i, c, pp);
3591 return eq(p, pp);
3592 }
3593
lcpolynomial::manager::imp3594 polynomial * lc(polynomial const * p, var x) {
3595 return coeff(p, x, degree(p, x));
3596 }
3597
gcd_prspolynomial::manager::imp3598 void gcd_prs(polynomial const * u, polynomial const * v, var x, polynomial_ref & r) {
3599 TRACE("polynomial_gcd", tout << "gcd prs with x" << x << " for\nu: ";
3600 u->display(tout, m_manager); tout << "\nv: "; v->display(tout, m_manager); tout << "\n";);
3601 if (degree(u, x) < degree(v, x))
3602 std::swap(u, v);
3603 scoped_numeral i_u(m_manager), i_v(m_manager);
3604 polynomial_ref c_u(pm()), c_v(pm());
3605 polynomial_ref pp_u(pm()), pp_v(pm());
3606 scoped_numeral d_a(m_manager);
3607 polynomial_ref d_r(pm());
3608 polynomial_ref g(pm()), h(pm()), rem(pm()), new_h(pm());
3609
3610 iccp(u, x, i_u, c_u, pp_u);
3611 iccp(v, x, i_v, c_v, pp_v);
3612
3613 gcd(c_u, c_v, d_r);
3614 m_manager.gcd(i_u, i_v, d_a);
3615 TRACE("polynomial_gcd_detail",
3616 tout << "After GCD of the content\n";
3617 tout << "u: "; u->display(tout, m_manager); tout << "\n";
3618 tout << "v: "; v->display(tout, m_manager); tout << "\n";
3619 tout << "i_u: " << i_u << "\n";
3620 tout << "i_v: " << i_v << "\n";
3621 tout << "c_u: " << c_u << "\n";
3622 tout << "c_v: " << c_v << "\n";
3623 tout << "pp_u: " << pp_u << "\n";
3624 tout << "pp_v: " << pp_v << "\n";
3625 tout << "d_r: " << d_r << "\nd_a: " << d_a << "\n";);
3626
3627 g = mk_one();
3628 h = mk_one();
3629
3630 unsigned counter = 0;
3631 while (true) {
3632 SASSERT(degree(pp_u, x) >= degree(pp_v, x));
3633 unsigned delta = degree(pp_u, x) - degree(pp_v, x);
3634 TRACE("polynomial_gcd_detail",
3635 tout << "iteration: " << counter << "\n";
3636 tout << "gcd loop\npp_u: " << pp_u << "\npp_v: " << pp_v << "\ndelta: " << delta << "\n";);
3637 counter++;
3638 exact_pseudo_remainder(pp_u, pp_v, x, rem);
3639 if (is_zero(rem)) {
3640 TRACE("polynomial", tout << "rem is zero...\npp_v: " << pp_v << "\n";);
3641 flip_sign_if_lm_neg(pp_v);
3642 r = pp(pp_v, x);
3643 r = mul(d_a, d_r, r);
3644 return;
3645 }
3646 if (is_const(rem)) {
3647 TRACE("polynomial", tout << "rem is a constant: " << rem << "\nr: " << d_r << "\nd_a: " << d_a << "\n";);
3648 r = mul(d_a, d_r);
3649 return;
3650 }
3651 pp_u = pp_v;
3652 // pp_v <- rem/g*h^{delta}
3653 pp_v = exact_div(rem, g);
3654 // delta is usually a small number, so I do not compute h^delta
3655 for (unsigned i = 0; i < delta; i++)
3656 pp_v = exact_div(pp_v, h);
3657 g = lc(pp_u, x);
3658 // h <- h^{1-delta}*g^{delta}
3659 new_h = mk_one();
3660 for (unsigned i = 0; i < delta; i++)
3661 new_h = mul(new_h, g);
3662 if (delta > 1) {
3663 for (unsigned i = 0; i < delta - 1; i++)
3664 new_h = exact_div(new_h, h);
3665 }
3666 h = new_h;
3667 }
3668 }
3669
3670 // Store in r <- gcd(content(u, x), v)
gcd_contentpolynomial::manager::imp3671 void gcd_content(polynomial const * u, var x, polynomial const * v, polynomial_ref & r) {
3672 scoped_numeral i_u(m_manager);
3673 polynomial_ref c_u(pm());
3674 polynomial_ref pp_u(pm());
3675
3676 iccp(u, x, i_u, c_u, pp_u);
3677 c_u = mul(i_u, c_u);
3678 gcd(c_u, v, r);
3679 }
3680
3681 // TODO: implement euclid algorithm when m_manager in Zp mode
euclid_gcdpolynomial::manager::imp3682 void euclid_gcd(polynomial const * u, polynomial const * v, polynomial_ref & r) {
3683 SASSERT(m().modular());
3684 CTRACE("mgcd", !is_univariate(u) || !is_univariate(v),
3685 tout << "euclid_gcd, polynomials are not univariate\n"; u->display(tout, m()); tout << "\n"; v->display(tout, m()); tout << "\n";);
3686 SASSERT(is_univariate(u));
3687 SASSERT(is_univariate(v));
3688 if (is_zero(u)) {
3689 r = const_cast<polynomial*>(v);
3690 flip_sign_if_lm_neg(r);
3691 return;
3692 }
3693 if (is_zero(v)) {
3694 r = const_cast<polynomial*>(u);
3695 flip_sign_if_lm_neg(r);
3696 return;
3697 }
3698 if (u == v) {
3699 r = const_cast<polynomial*>(u);
3700 flip_sign_if_lm_neg(r);
3701 return;
3702 }
3703 if (is_const(u) || is_const(v)) {
3704 scoped_numeral i_u(m_manager), i_v(m_manager);
3705 ic(v, i_v);
3706 ic(u, i_u);
3707 scoped_numeral a(m_manager);
3708 m_manager.gcd(i_v, i_u, a);
3709 r = mk_const(a);
3710 return;
3711 }
3712 // Maybe map it to univariate case
3713 gcd_prs(u, v, max_var(u), r);
3714 }
3715
3716 // Combine two different modular images using Chinese Remainder theorem
3717 // The new bound is stored in b2
CRA_combine_imagespolynomial::manager::imp3718 void CRA_combine_images(polynomial const * C1, scoped_numeral const & b1, polynomial const * C2, scoped_numeral & b2, polynomial_ref & r) {
3719 lex_sort(C1);
3720 lex_sort(C2);
3721 TRACE("CRA", tout << "C1: "; C1->display(tout, m()); tout << "\nC2: "; C2->display(tout, m()); tout << "\n";);
3722 SASSERT(m_cheap_som_buffer.empty());
3723 SASSERT(!m().m().is_even(b1));
3724 SASSERT(!m().m().is_even(b2));
3725 cheap_som_buffer & R = m_cheap_som_buffer;
3726 scoped_numeral inv1(m());
3727 scoped_numeral inv2(m());
3728 scoped_numeral g(m());
3729 m().gcd(b1, b2, inv1, inv2, g);
3730 SASSERT(m().is_one(g));
3731 TRACE("CRA", tout << "b1: " << b1 << ", b2: " << b2 << ", inv1: " << inv1 << ", inv2: " << inv2 << "\n";);
3732 // b1*inv1 + b2.inv2 = 1
3733 // inv1 is the inverse of b1 mod b2
3734 // inv2 is the inverse of b2 mod b1
3735 m().m().mod(inv1, b2, inv1);
3736 m().m().mod(inv2, b1, inv2);
3737 TRACE("CRA", tout << "inv1: " << inv1 << ", inv2: " << inv2 << "\n";);
3738 scoped_numeral a1(m());
3739 scoped_numeral a2(m());
3740 m().mul(b2, inv2, a1); // a1 is the multiplicator for coefficients of C1
3741 m().mul(b1, inv1, a2); // a2 is the multiplicator for coefficients of C2
3742 TRACE("CRA", tout << "a1: " << a1 << ", a2: " << a2 << "\n";);
3743 // new bound
3744 scoped_numeral new_bound(m());
3745 m().mul(b1, b2, new_bound);
3746 scoped_numeral lower(m());
3747 scoped_numeral upper(m());
3748 scoped_numeral new_a(m()), tmp1(m()), tmp2(m()), tmp3(m());
3749 m().div(new_bound, 2, upper);
3750 m().set(lower, upper);
3751 m().neg(lower);
3752 TRACE("CRA", tout << "lower: " << lower << ", upper: " << upper << "\n";);
3753
3754 #define ADD(A1, A2, M) { \
3755 m().mul(A1, a1, tmp1); \
3756 m().mul(A2, a2, tmp2); \
3757 m().add(tmp1, tmp2, tmp3); \
3758 m().m().mod(tmp3, new_bound, new_a); \
3759 if (m().gt(new_a, upper)) \
3760 m().sub(new_a, new_bound, new_a); \
3761 R.add(new_a, M); \
3762 }
3763
3764 numeral zero(0);
3765 unsigned i1 = 0;
3766 unsigned i2 = 0;
3767 unsigned sz1 = C1->size();
3768 unsigned sz2 = C2->size();
3769 while (true) {
3770 if (i1 == sz1) {
3771 while (i2 < sz2) {
3772 TRACE("CRA", tout << "adding C2 rest\n";);
3773 ADD(zero, C2->a(i2), C2->m(i2));
3774 i2++;
3775 }
3776 break;
3777 }
3778 if (i2 == sz2) {
3779 while (i1 < sz1) {
3780 TRACE("CRA", tout << "adding C1 rest\n";);
3781 ADD(C1->a(i1), zero, C1->m(i1));
3782 i1++;
3783 }
3784 break;
3785 }
3786 monomial * m1 = C1->m(i1);
3787 monomial * m2 = C2->m(i2);
3788 int s = lex_compare(m1, m2);
3789 if (s == 0) {
3790 ADD(C1->a(i1), C2->a(i2), m1);
3791 TRACE("CRA",
3792 tout << "C1->a(i1): " << m().to_string(C1->a(i1)) << ", C2->a(i2): " << m().to_string(C2->a(i2)) << ", new_a: " << new_a << "\n";
3793 tout << "tmp1: " << tmp1 << ", tmp2: " << tmp2 << ", tmp3: " << tmp3 << "\n";);
3794 i1++;
3795 i2++;
3796 }
3797 else if (s > 0) {
3798 TRACE("CRA", tout << "C1 mon biggerr, adding it...\n";);
3799 ADD(C1->a(i1), zero, m1);
3800 i1++;
3801 }
3802 else {
3803 TRACE("CRA", tout << "C2 mon bigger, adding it...\n";);
3804 ADD(zero, C2->a(i2), m2);
3805 i2++;
3806 }
3807 }
3808 m().set(b2, new_bound);
3809 r = R.mk();
3810 }
3811
uni_mod_gcdpolynomial::manager::imp3812 void uni_mod_gcd(polynomial const * u, polynomial const * v, polynomial_ref & r) {
3813 TRACE("mgcd", tout << "univ_modular_gcd\nu: "; u->display(tout, m_manager); tout << "\nv: "; v->display(tout, m_manager); tout << "\n";);
3814 SASSERT(!m().modular());
3815 SASSERT(is_univariate(u));
3816 SASSERT(!is_const(u) && !is_const(v));
3817 SASSERT(max_var(u) == max_var(v));
3818 var x = max_var(u);
3819 scoped_numeral c_u(m()), c_v(m());
3820 polynomial_ref pp_u(pm()), pp_v(pm());
3821 ic(u, c_u, pp_u);
3822 ic(v, c_v, pp_v);
3823
3824 scoped_numeral d_a(m());
3825 m_manager.gcd(c_u, c_v, d_a);
3826
3827 scoped_numeral lc_u(m());
3828 scoped_numeral lc_v(m());
3829 unsigned d_u = degree(pp_u, x);
3830 unsigned d_v = degree(pp_v, x);
3831 lc_u = univ_coeff(pp_u, d_u);
3832 lc_v = univ_coeff(pp_v, d_v);
3833 scoped_numeral lc_g(m());
3834 m().gcd(lc_u, lc_v, lc_g);
3835
3836 polynomial_ref u_Zp(m_wrapper);
3837 polynomial_ref v_Zp(m_wrapper);
3838
3839 polynomial_ref C_star(m_wrapper);
3840 scoped_numeral bound(m());
3841 polynomial_ref q(m_wrapper);
3842
3843 polynomial_ref candidate(m_wrapper);
3844
3845 scoped_numeral p(m());
3846 for (unsigned i = 0; i < NUM_BIG_PRIMES; i++) {
3847 m().set(p, g_big_primes[i]);
3848 TRACE("mgcd", tout << "trying prime: " << p << "\n";);
3849 {
3850 scoped_set_zp setZp(m_wrapper, p);
3851 u_Zp = normalize(pp_u);
3852 v_Zp = normalize(pp_v);
3853 if (degree(u_Zp, x) < d_u) {
3854 TRACE("mgcd", tout << "bad prime, leading coefficient vanished\n";);
3855 continue; // bad prime
3856 }
3857 if (degree(v_Zp, x) < d_v) {
3858 TRACE("mgcd", tout << "bad prime, leading coefficient vanished\n";);
3859 continue; // bad prime
3860 }
3861 euclid_gcd(u_Zp, v_Zp, q);
3862 // normalize so that lc_g is leading coefficient of q
3863 q = mk_glex_monic(q);
3864 scoped_numeral c(m());
3865 m().set(c, lc_g);
3866 q = mul(c, q);
3867 }
3868 TRACE("mgcd", tout << "new q:\n" << q << "\n";);
3869 if (is_const(q)) {
3870 TRACE("mgcd", tout << "done, modular gcd is one\n";);
3871 if (m().is_one(d_a))
3872 r = q; // GCD is one
3873 else
3874 r = mk_const(d_a);
3875 return;
3876 }
3877 if (C_star.get() == nullptr) {
3878 C_star = q;
3879 m().set(bound, p);
3880 }
3881 else {
3882 if (degree(q, x) < degree(C_star, x)) {
3883 // discard accumulated image, it was affected by unlucky primes
3884 TRACE("mgcd", tout << "discarding image\n";);
3885 C_star = q;
3886 m().set(bound, p);
3887 }
3888 else {
3889 CRA_combine_images(q, p, C_star, bound, C_star);
3890 TRACE("mgcd", tout << "new combined:\n" << C_star << "\n";);
3891 }
3892 }
3893 candidate = pp(C_star, x);
3894 TRACE("mgcd", tout << "candidate:\n" << candidate << "\n";);
3895 scoped_numeral lc_candidate(m());
3896 lc_candidate = univ_coeff(candidate, degree(candidate, x));
3897 if (m().divides(lc_candidate, lc_g) &&
3898 divides(candidate, pp_u) &&
3899 divides(candidate, pp_v)) {
3900 TRACE("mgcd", tout << "found GCD\n";);
3901 r = mul(d_a, candidate);
3902 flip_sign_if_lm_neg(r);
3903 TRACE("mgcd", tout << "r: " << r << "\n";);
3904 return;
3905 }
3906 }
3907 // Oops, modular GCD failed, not enough primes
3908 // fallback to prs
3909 gcd_prs(u, v, x, r);
3910 }
3911
3912 typedef ref_buffer<polynomial, manager> polynomial_ref_buffer;
3913
3914 /**
3915 Compute the content and primitive parts of p, when p is viewed as a multivariate polynomial Zp[y_1, ..., y_n]
3916 with coefficients in Zp[x].
3917 */
3918 som_buffer_vector m_iccp_ZpX_buffers;
iccp_ZpXpolynomial::manager::imp3919 void iccp_ZpX(polynomial const * p, var x, numeral & ci, polynomial_ref & c, polynomial_ref & pp) {
3920 SASSERT(m().modular());
3921 TRACE("mgcd_detail", tout << "iccp_ZpX, p: "; p->display(tout, m()); tout << "\nvar x" << x << "\n";);
3922 if (is_zero(p)) {
3923 TRACE("mgcd_detail", tout << "iccp_ZpX, p is zero\n";);
3924 m_manager.set(ci, 0);
3925 c = mk_one();
3926 pp = const_cast<polynomial*>(p);
3927 return;
3928 }
3929 if (is_const(p)) {
3930 TRACE("mgcd_detail", tout << "iccp_ZpX, p is constant\n";);
3931 m_manager.set(ci, p->a(0));
3932 c = mk_one();
3933 pp = mk_one();
3934 return;
3935 }
3936 unsigned d = degree(p, x);
3937 if (d == 0) {
3938 TRACE("mgcd_detail", tout << "iccp_ZpX, degree(p, x) == 0\n";);
3939 ic(p, ci, pp);
3940 c = mk_one();
3941 return;
3942 }
3943 // 1) traverse monomials of p, and mark the monomials that contain p, also compute the minimal degree of x in p.
3944 ref_buffer<monomial, manager> no_x_ms(m_wrapper); // monomials that do not contains x
3945 unsigned min_degree = UINT_MAX; // min degree of x in p
3946 unsigned sz = p->size();
3947 for (unsigned i = 0; i < sz; i++) {
3948 monomial * m = p->m(i);
3949 unsigned k = m->degree_of(x);
3950 if (k == 0) {
3951 // if m is not marked
3952 if (m_m2pos.get(m) == UINT_MAX) {
3953 no_x_ms.push_back(m);
3954 m_m2pos.set(m, 1); // it is just a mark
3955 }
3956 }
3957 if (k < min_degree)
3958 min_degree = k;
3959 }
3960 SASSERT(min_degree == 0 || no_x_ms.empty());
3961 if (min_degree > 0) {
3962 SASSERT(no_x_ms.empty());
3963 // nothing was marked.
3964 // divide by x^min_degree
3965 TRACE("mgcd_detail", tout << "iccp_ZpX, all monomials contain x" << x << ", dividing by x" << x << "^" << min_degree << "\n";);
3966 polynomial_ref xmin(m_wrapper);
3967 polynomial_ref new_p(m_wrapper);
3968 xmin = mk_polynomial(x, min_degree);
3969 new_p = exact_div(p, xmin);
3970 iccp_ZpX(new_p, x, ci, c, pp);
3971 c = mul(xmin, c);
3972 return;
3973 }
3974 // 2) if for some marked monomial m (i.e., the ones that do not contain x), there is no monomial m*x^k in p,
3975 // then c = 1
3976 unsigned num_marked = no_x_ms.size();
3977 unsigned num_unmarked = 0;
3978 monomial_ref tmp_m(m_wrapper);
3979 for (unsigned i = 0; i < sz; i++) {
3980 monomial * m = p->m(i);
3981 unsigned k = m->degree_of(x);
3982 if (k == 0)
3983 continue;
3984 tmp_m = div_x(m, x);
3985 SASSERT(tmp_m != m); // since x is in m, but not in tmp_m
3986 if (m_m2pos.get(tmp_m) == 1) {
3987 num_unmarked++;
3988 m_m2pos.reset(tmp_m);
3989 SASSERT(m_m2pos.get(tmp_m) == UINT_MAX);
3990 }
3991 }
3992 SASSERT(num_unmarked <= num_marked);
3993 if (num_unmarked < num_marked) {
3994 // reset remaining marks
3995 for (unsigned i = 0; i < num_marked; i++)
3996 m_m2pos.reset(no_x_ms[i]);
3997 TRACE("mgcd_detail", tout << "iccp_ZpX, cheap case... invoking ic\n";);
3998 ic(p, ci, pp);
3999 c = mk_one();
4000 return;
4001 }
4002 // 3) expensive case
4003 // Basic idea: separate a*m*x^k into a*x^k and m, put a*x^k into the som_buffer associated with m.
4004 // The mapping between m is som_buffers is given by m_m2pos
4005
4006 // Extract integer content
4007 ic(p, ci, pp);
4008 no_x_ms.reset();
4009 som_buffer_vector & som_buffers = m_iccp_ZpX_buffers;
4010 som_buffers.set_owner(this);
4011 for (unsigned i = 0; i < sz; i++) {
4012 monomial * m = pp->m(i);
4013 unsigned k = m->degree_of(x);
4014 if (k != 0) {
4015 tmp_m = div_x(m, x);
4016 m = tmp_m.get();
4017 }
4018 unsigned pos = m_m2pos.get(m);
4019 if (pos == UINT_MAX) {
4020 pos = no_x_ms.size();
4021 no_x_ms.push_back(m);
4022 m_m2pos.set(m, pos);
4023 }
4024 som_buffer * som = som_buffers[pos];
4025 som->add(pp->a(i), mk_monomial(x, k));
4026 }
4027 unsigned num_ms = no_x_ms.size();
4028 for (unsigned i = 0; i < num_ms; i++)
4029 m_m2pos.reset(no_x_ms[i]);
4030 SASSERT(num_ms > 0);
4031 // Compute GCD of all som_buffers
4032 polynomial_ref g(m_wrapper);
4033 polynomial_ref new_g(m_wrapper);
4034 g = som_buffers[0]->mk();
4035 for (unsigned i = 1; i < num_ms; i++) {
4036 polynomial_ref a(m_wrapper);
4037 a = som_buffers[i]->mk();
4038 SASSERT(is_univariate(a));
4039 euclid_gcd(g, a, new_g);
4040 g = new_g;
4041 if (is_const(g))
4042 break;
4043 }
4044 if (!is_const(g)) {
4045 CTRACE("content_bug", !divides(g, pp),
4046 tout << "GF(" << m().m().to_string(m().p()) << ")\n";
4047 tout << "pp: "; pp->display(tout, m()); tout << "\n"; tout << "var: x" << x << "\n";
4048 tout << "content: " << g << "\n";);
4049 c = g;
4050 pp = exact_div(pp, c);
4051 }
4052 else {
4053 c = mk_one();
4054 }
4055 som_buffers.reset(num_ms);
4056 }
4057
4058 // Return the leading coefficient (with respect to glex) of p when
4059 // p is viewed as a multivariate polynomial Zp[y_1, ..., y_n] with coefficients in Zp[x].
lc_glex_ZpXpolynomial::manager::imp4060 polynomial * lc_glex_ZpX(polynomial const * p, var x) {
4061 // collect a*x^k of maximal monomial with respect to glex
4062 m_som_buffer.reset();
4063 monomial_ref max_m(m_wrapper);
4064 monomial_ref tmp_m(m_wrapper);
4065 unsigned sz = p->size();
4066 for (unsigned i = 0; i < sz; i++) {
4067 monomial * m = p->m(i);
4068 unsigned k = m->degree_of(x);
4069 if (k != 0) {
4070 tmp_m = div_x(m, x);
4071 m = tmp_m.get();
4072 }
4073 if (max_m == 0 || graded_lex_compare(m, max_m) > 0) {
4074 // found new maximal monomial
4075 m_som_buffer.reset();
4076 max_m = m;
4077 m_som_buffer.add(p->a(i), mk_monomial(x, k));
4078 }
4079 else if (max_m == m) {
4080 // found another a*x^k of max_m
4081 m_som_buffer.add(p->a(i), mk_monomial(x, k));
4082 }
4083 }
4084 SASSERT(!m_som_buffer.empty());
4085 TRACE("mgcd_detail", tout << "maximal monomial: "; max_m->display(tout); tout << "\n";);
4086 return m_som_buffer.mk();
4087 }
4088
4089 // Wrapper for iccp_ZpX
primitive_ZpXpolynomial::manager::imp4090 void primitive_ZpX(polynomial const * p, var x, polynomial_ref & pp) {
4091 scoped_numeral ci(m());
4092 polynomial_ref c(m_wrapper);
4093 iccp_ZpX(p, x, ci, c, pp);
4094 }
4095
4096 // select a new random value in GF(p) that is not in vals, and store it in r
peek_freshpolynomial::manager::imp4097 void peek_fresh(scoped_numeral_vector const & vals, unsigned p, scoped_numeral & r) {
4098 SASSERT(vals.size() < p); // otherwise we can't keep the fresh value
4099 auto sz = vals.size();
4100 while (true) {
4101 m().set(r, rand() % p);
4102 // check if fresh value...
4103 unsigned k = 0;
4104 for (; k < sz; k++) {
4105 if (m().eq(vals[k], r))
4106 break;
4107 }
4108 if (k == sz)
4109 return; // value is fresh
4110 }
4111 }
4112
4113 newton_interpolator_vector m_mgcd_iterpolators;
4114 scoped_ptr_vector<skeleton> m_mgcd_skeletons;
4115
4116 struct sparse_mgcd_failed {};
4117
4118 // Auxiliary recursive function used in multivariate modular GCD
mod_gcd_recpolynomial::manager::imp4119 void mod_gcd_rec(polynomial const * u, polynomial const * v, unsigned p,
4120 unsigned idx, var_buffer const & vars, polynomial_ref & r) {
4121 TRACE("mgcd", tout << "mod_gcd_rec\nu: "; u->display(tout, m_manager, true); tout << "\nv: "; v->display(tout, m_manager, true); tout << "\n";);
4122 unsigned num_vars = vars.size();
4123 SASSERT(num_vars > 0);
4124 if (idx == num_vars - 1) {
4125 SASSERT(is_univariate(u));
4126 SASSERT(is_univariate(v));
4127 euclid_gcd(u, v, r);
4128 TRACE("mgcd", tout << "mod_gcd_rec result: "; r->display(tout, m_manager, true); tout << "\n";);
4129 return;
4130 }
4131
4132 var x = vars[idx];
4133 scoped_numeral ci_u(m()), ci_v(m());
4134 polynomial_ref c_u(m_wrapper), pp_u(m_wrapper), lc_u(m_wrapper);
4135 polynomial_ref c_v(m_wrapper), pp_v(m_wrapper), lc_v(m_wrapper);
4136 iccp_ZpX(u, x, ci_u, c_u, pp_u);
4137 iccp_ZpX(v, x, ci_v, c_v, pp_v);
4138 lc_u = lc_glex_ZpX(pp_u, x);
4139 lc_v = lc_glex_ZpX(pp_v, x);
4140 scoped_numeral ci_g(m());
4141 polynomial_ref c_g(m_wrapper);
4142 polynomial_ref lc_g(m_wrapper);
4143 TRACE("mgcd_detail",
4144 tout << "idx: " << idx << "\n";
4145 tout << "x" << x << "\n";
4146 tout << "pp_u = "; pp_u->display(tout, m_manager, true); tout << "\n";
4147 tout << "pp_v = "; pp_v->display(tout, m_manager, true); tout << "\n";
4148 tout << "c_u = "; c_u->display(tout, m_manager, true); tout << "\n";
4149 tout << "c_v = "; c_v->display(tout, m_manager, true); tout << "\n";
4150 tout << "lc_u = "; lc_u->display(tout, m_manager, true); tout << "\n";
4151 tout << "lc_v = "; lc_v->display(tout, m_manager, true); tout << "\n";
4152 tout << "ci_u = " << ci_u << "\n";
4153 tout << "ci_v = " << ci_v << "\n";);
4154 m().gcd(ci_u, ci_v, ci_g);
4155 euclid_gcd(c_u, c_v, c_g);
4156 euclid_gcd(lc_u, lc_v, lc_g);
4157 TRACE("mgcd_detail",
4158 tout << "c_g = "; c_g->display(tout, m_manager, true); tout << "\n";
4159 tout << "lc_g = "; lc_g->display(tout, m_manager, true); tout << "\n";
4160 tout << "ci_g = " << ci_g << "\n";);
4161
4162 skeleton * sk = m_mgcd_skeletons[idx];
4163
4164 // use dense interpolation if skeleton is not available
4165 newton_interpolator & interpolator = m_mgcd_iterpolators[idx];
4166 sparse_interpolator sinterpolator(sk);
4167
4168 polynomial_ref u1(m_wrapper), v1(m_wrapper), q(m_wrapper);
4169 scoped_numeral val(m());
4170 scoped_numeral lc_g_val(m());
4171 polynomial_ref H(m_wrapper), C(m_wrapper);
4172 polynomial_ref lc_H(m_wrapper);
4173 unsigned min_deg_q = UINT_MAX;
4174 unsigned counter = 0;
4175
4176 for (;; counter++) {
4177 while (true) {
4178 peek_fresh(interpolator.inputs(), p, val);
4179 // the selected value must satisfy lc_g(val) != 0
4180 univ_eval(lc_g, x, val, lc_g_val);
4181 if (!m().is_zero(lc_g_val))
4182 break;
4183 }
4184 TRACE("mgcd", tout << "x" << x << " -> " << val << "\n";);
4185 u1 = substitute(pp_u, 1, &x, &(val.get()));
4186 v1 = substitute(pp_v, 1, &x, &(val.get()));
4187 mod_gcd_rec(u1, v1, p, idx+1, vars, q);
4188 q = mk_glex_monic(q);
4189 q = mul(lc_g_val, q);
4190 var q_var = max_var(q);
4191 unsigned deg_q = q_var == null_var ? 0 : degree(q, q_var);
4192 TRACE("mgcd_detail", tout << "counter: " << counter << "\nidx: " << idx << "\nq: " << q << "\ndeg_q: " << deg_q << "\nmin_deg_q: " <<
4193 min_deg_q << "\nnext_x: x" << vars[idx+1] << "\nmax_var(q): " << q_var << "\n";);
4194 if (deg_q < min_deg_q) {
4195 TRACE("mgcd_detail", tout << "resetting...\n";);
4196 counter = 0;
4197 min_deg_q = deg_q;
4198 // start from scratch
4199 if (sk == nullptr) {
4200 interpolator.reset();
4201 interpolator.add(val, q);
4202 }
4203 else {
4204 sinterpolator.reset();
4205 if (!sinterpolator.add(val, q))
4206 throw sparse_mgcd_failed();
4207 }
4208 }
4209 else if (deg_q == min_deg_q) {
4210 TRACE("mgcd_detail", tout << "adding sample point...\n";);
4211 if (sk == nullptr) {
4212 interpolator.add(val, q);
4213 }
4214 else {
4215 if (!sinterpolator.add(val, q))
4216 throw sparse_mgcd_failed();
4217 }
4218 }
4219 else {
4220 TRACE("mgcd", tout << "skipping q...\n";);
4221 continue;
4222 }
4223 bool found_candidate = false;
4224 if (sk == nullptr) {
4225 SASSERT(interpolator.num_sample_points() > 0);
4226 interpolator.mk(x, H);
4227 TRACE("mgcd_detail", tout << "idx: " << idx << "\ncandidate H: " << H << "\n";);
4228 lc_H = lc_glex_ZpX(H, x);
4229 TRACE("mgcd_detail", tout << "idx: " << idx << "\nlc_H: " << lc_H << "\nlc_g: " << lc_g << "\n";);
4230 if (eq(lc_H, lc_g)) {
4231 found_candidate = true;
4232 }
4233 }
4234 else {
4235 if (sinterpolator.ready()) {
4236 if (!sinterpolator.mk(H))
4237 throw sparse_mgcd_failed();
4238 found_candidate = true;
4239 }
4240 }
4241
4242 bool done = false;
4243 if (found_candidate) {
4244 if (degree(H, x) > 0)
4245 primitive_ZpX(H, x, C);
4246 else
4247 C = normalize(H);
4248 TRACE("mgcd_detail", tout << "C: " << C << "\npp_u: " << pp_u << "\npp_v: " << pp_v << "\ndivides(C, pp_u): " <<
4249 divides(C, pp_u) << "\ndivides(C, pp_v): " << divides(C, pp_v) << "\n";);
4250 if (divides(C, pp_u) && divides(C, pp_v)) {
4251 r = mul(c_g, C);
4252 r = mul(ci_g, r);
4253 done = true;
4254 }
4255 else if (min_deg_q == 0) {
4256 r = c_g;
4257 r = mul(ci_g, r);
4258 done = true;
4259 }
4260 else if (sk != nullptr) {
4261 throw sparse_mgcd_failed();
4262 }
4263 }
4264
4265 if (done) {
4266 TRACE("mgcd", tout << "idx: " << idx << "\nresult: " << r << "\n";);
4267 if (sk == nullptr && m_use_sparse_gcd) {
4268 // save skeleton
4269 skeleton * new_sk = alloc(skeleton, *this, H, x);
4270 m_mgcd_skeletons.set(idx, new_sk);
4271 }
4272 return;
4273 }
4274 }
4275 }
4276
4277 // Multivariate modular GCD algorithm
mod_gcdpolynomial::manager::imp4278 void mod_gcd(polynomial const * u, polynomial const * v,
4279 power_buffer const & u_var_degrees, power_buffer const & v_var_degrees,
4280 polynomial_ref & r) {
4281 m_mgcd_iterpolators.set_owner(this);
4282 TRACE("mgcd", tout << "mod_gcd\nu: "; u->display(tout, m_manager, true); tout << "\nv: "; v->display(tout, m_manager, true); tout << "\n";);
4283 TRACE("mgcd_call", tout << "mod_gcd\nu: "; u->display(tout, m_manager, true); tout << "\nv: "; v->display(tout, m_manager, true); tout << "\n";);
4284 SASSERT(!m().modular());
4285 // u and v contain the same set of variables
4286 SASSERT(u_var_degrees.size() == v_var_degrees.size());
4287 unsigned num_vars = u_var_degrees.size();
4288 SASSERT(num_vars > 1); // should use uni_mod_gcd if univariate
4289 var_buffer vars;
4290 power_buffer var_min_degrees;
4291 for (unsigned i = 0; i < num_vars; i++) {
4292 SASSERT(u_var_degrees[i].get_var() == v_var_degrees[i].get_var());
4293 var x = u_var_degrees[i].get_var();
4294 unsigned d = std::min(u_var_degrees[i].degree(), v_var_degrees[i].degree());
4295 var_min_degrees.push_back(power(x, d));
4296 }
4297 std::sort(var_min_degrees.begin(), var_min_degrees.end(), power::lt_degree());
4298 m_mgcd_skeletons.reset();
4299 for (unsigned i = 0; i < num_vars; i++) {
4300 vars.push_back(var_min_degrees[i].get_var());
4301 m_mgcd_skeletons.push_back(nullptr);
4302 }
4303
4304 scoped_numeral c_u(m()), c_v(m());
4305 polynomial_ref pp_u(pm()), pp_v(pm());
4306 ic(u, c_u, pp_u);
4307 ic(v, c_v, pp_v);
4308
4309 scoped_numeral d_a(m());
4310 m_manager.gcd(c_u, c_v, d_a);
4311
4312 unsigned mm_u_pos = pp_u->graded_lex_max_pos(); // position of the maximal monomial in u
4313 unsigned mm_v_pos = pp_v->graded_lex_max_pos(); // position of the maximal monomial in v
4314 scoped_numeral lc_u(m());
4315 scoped_numeral lc_v(m());
4316 lc_u = pp_u->a(mm_u_pos);
4317 lc_v = pp_v->a(mm_v_pos);
4318 scoped_numeral lc_g(m());
4319 m().gcd(lc_u, lc_v, lc_g);
4320
4321 polynomial_ref u_Zp(m_wrapper);
4322 polynomial_ref v_Zp(m_wrapper);
4323 polynomial_ref C_star(m_wrapper);
4324 scoped_numeral bound(m());
4325 polynomial_ref q(m_wrapper);
4326 polynomial_ref candidate(m_wrapper);
4327 scoped_numeral p(m());
4328
4329 for (unsigned i = 0; i < NUM_BIG_PRIMES; i++) {
4330 m().set(p, g_big_primes[i]);
4331 TRACE("mgcd", tout << "trying prime: " << p << "\n";);
4332 {
4333 scoped_set_zp setZp(m_wrapper, p);
4334 u_Zp = normalize(pp_u);
4335 if (u_Zp->size() != pp_u->size()) {
4336 TRACE("mgcd", tout << "bad prime, coefficient(s) vanished\n";);
4337 continue; // bad prime some monomial vanished
4338 }
4339 v_Zp = normalize(pp_v);
4340 if (v_Zp->size() != pp_v->size()) {
4341 TRACE("mgcd", tout << "bad prime, coefficient(s) vanished\n";);
4342 continue; // bad prime some monomial vanished
4343 }
4344 TRACE("mgcd", tout << "u_Zp: " << u_Zp << "\nv_Zp: " << v_Zp << "\n";);
4345 mod_gcd_rec(u_Zp, v_Zp, g_big_primes[i], 0, vars, q);
4346 q = mk_glex_monic(q);
4347 scoped_numeral c(m());
4348 m().set(c, lc_g);
4349 q = mul(c, q);
4350 }
4351 TRACE("mgcd", tout << "new q:\n" << q << "\n";);
4352 if (is_const(q)) {
4353 TRACE("mgcd", tout << "done, modular gcd is one\n";);
4354 r = mk_const(d_a);
4355 return;
4356 }
4357 if (C_star.get() == nullptr) {
4358 C_star = q;
4359 m().set(bound, p);
4360 }
4361 else {
4362 monomial * max_C_star = C_star->m(C_star->graded_lex_max_pos());
4363 monomial * max_q = q->m(q->graded_lex_max_pos());
4364 if (graded_lex_compare(max_q, max_C_star) < 0) {
4365 // Discard accumulated image, it was affected by unlucky primes
4366 // maximal monomial of q is smaller than maximal monomial of C_star
4367 TRACE("mgcd", tout << "discarding image\n";);
4368 C_star = q;
4369 m().set(bound, p);
4370 }
4371 else {
4372 CRA_combine_images(q, p, C_star, bound, C_star);
4373 TRACE("mgcd", tout << "new combined:\n" << C_star << "\n";);
4374 }
4375 }
4376 candidate = normalize(C_star);
4377 TRACE("mgcd", tout << "candidate:\n" << candidate << "\n";);
4378 scoped_numeral lc_candidate(m());
4379 lc_candidate = candidate->a(candidate->graded_lex_max_pos());
4380 if (m().divides(lc_candidate, lc_g) &&
4381 divides(candidate, pp_u) &&
4382 divides(candidate, pp_v)) {
4383 TRACE("mgcd", tout << "found GCD\n";);
4384 r = mul(d_a, candidate);
4385 flip_sign_if_lm_neg(r);
4386 TRACE("mgcd", tout << "r: " << r << "\n";);
4387 return;
4388 }
4389 }
4390 // Oops, modular GCD failed, not enough primes
4391 // fallback to prs
4392 gcd_prs(u, v, max_var(u), r);
4393 }
4394
gcdpolynomial::manager::imp4395 void gcd(polynomial const * u, polynomial const * v, polynomial_ref & r) {
4396 power_buffer u_var_degrees;
4397 power_buffer v_var_degrees;
4398 TRACE("gcd_calls", tout << "gcd\nu: "; u->display(tout, m_manager); tout << "\nv: "; v->display(tout, m_manager); tout << "\n";);
4399 TRACE("polynomial_gcd",
4400 tout << "gcd\nu: "; u->display(tout, m_manager); tout << "\nv: "; v->display(tout, m_manager);
4401 tout << "\nis_zero(u): " << is_zero(u) << ", is_const(u): " << is_const(u) << "\n";
4402 tout << "is_zero(v): " << is_zero(v) << ", is_const(v): " << is_const(v) << "\n";
4403 tout << "modular: " << m().modular() << "\n";);
4404 if (is_zero(u)) {
4405 r = const_cast<polynomial*>(v);
4406 flip_sign_if_lm_neg(r);
4407 return;
4408 }
4409 if (is_zero(v)) {
4410 r = const_cast<polynomial*>(u);
4411 flip_sign_if_lm_neg(r);
4412 return;
4413 }
4414 if (u == v) {
4415 r = const_cast<polynomial*>(u);
4416 flip_sign_if_lm_neg(r);
4417 return;
4418 }
4419 if (is_const(u) || is_const(v)) {
4420 scoped_numeral i_u(m_manager), i_v(m_manager);
4421 ic(v, i_v);
4422 ic(u, i_u);
4423 scoped_numeral a(m_manager);
4424 m_manager.gcd(i_v, i_u, a);
4425 r = mk_const(a);
4426 return;
4427 }
4428
4429 // Search for a variable x that occurs only in u or v.
4430 var_max_degrees(u, u_var_degrees); std::sort(u_var_degrees.begin(), u_var_degrees.end(), power::lt_var());
4431 var_max_degrees(v, v_var_degrees); std::sort(v_var_degrees.begin(), v_var_degrees.end(), power::lt_var());
4432
4433 TRACE("polynomial_gcd",
4434 tout << "u var info\n"; for (unsigned i = 0; i < u_var_degrees.size(); i++) tout << u_var_degrees[i] << " "; tout << "\n";
4435 tout << "v var info\n"; for (unsigned i = 0; i < v_var_degrees.size(); i++) tout << v_var_degrees[i] << " "; tout << "\n";);
4436 var x = null_var;
4437 bool u_found = false;
4438 bool v_found = false;
4439 unsigned i = 0;
4440 unsigned u_sz = u_var_degrees.size();
4441 unsigned v_sz = v_var_degrees.size();
4442 unsigned sz = std::min(u_sz, v_sz);
4443 for (; i < sz; i++) {
4444 var xu = u_var_degrees[i].get_var();
4445 var xv = v_var_degrees[i].get_var();
4446 if (xu < xv) {
4447 x = xu;
4448 u_found = true;
4449 break;
4450 }
4451 if (xu > xv) {
4452 x = xv;
4453 v_found = true;
4454 break;
4455 }
4456 }
4457 if (!u_found && !v_found && i < u_sz) {
4458 x = u_var_degrees[i].get_var();
4459 u_found = true;
4460 }
4461 if (!u_found && !v_found && i < v_sz) {
4462 x = v_var_degrees[i].get_var();
4463 v_found = true;
4464 }
4465
4466 if (u_found) {
4467 // u has a variable x that v doesn't have.
4468 // Thus, gcd(u, v) = gcd(content(u, x), v)
4469 gcd_content(u, x, v, r);
4470 return;
4471 }
4472
4473 if (v_found) {
4474 // v has a variable x that u doesn't have.
4475 // Thus, gcd(u, v) = gcd(u, content(v, x))
4476 gcd_content(v, x, u, r);
4477 return;
4478 }
4479
4480 // TODO:
4481 // Try to find a variable x that occurs linearly in u or v
4482 // In this case, the GCD is linear or constant in x.
4483 // Assume x occurs linearly in u. Then,
4484 // gcd(u, v) = gcd(content(u, x), content(v, x)) if pp(u, x) does not divide pp(v, x)
4485 // gcd(u, v) = gcd(content(u, x), content(v, x))*pp(u,x) if pp(u, x) divides pp(v, x)
4486 //
4487
4488 // select variable with minimal degree
4489 x = u_var_degrees[sz - 1].get_var(); // give preference to maximal variable
4490 SASSERT(u_var_degrees[sz - 1].get_var() == v_var_degrees[sz - 1].get_var());
4491 SASSERT(max_var(u) == max_var(v));
4492 SASSERT(max_var(u) == x);
4493 #if 0
4494 unsigned min_k = std::max(m_u_var_degrees[sz - 1].degree(), m_v_var_degrees[sz - 1].degree());
4495 unsigned max_var_bias = 2; // the basic procedures are optimized for operating on the maximal variable. So, we have a bias to select the maximal one
4496 if (min_k > max_var_bias) {
4497 min_k -= max_var_bias;
4498 i = sz - 1;
4499 while (i > 0) {
4500 --i;
4501 SASSERT(m_u_var_degrees[i].get_var() == m_v_var_degrees[i].get_var());
4502 unsigned k = std::max(m_u_var_degrees[i].degree(), m_v_var_degrees[i].degree());
4503 if (k < min_k) {
4504 x = m_u_var_degrees[i].get_var();
4505 min_k = k;
4506 }
4507 }
4508 }
4509 #endif
4510 if (!m().modular() && !m_use_prs_gcd) {
4511 SASSERT(max_var(u) == max_var(v));
4512 if (is_univariate(u)) {
4513 SASSERT(is_univariate(v));
4514 uni_mod_gcd(u, v, r);
4515 }
4516 else {
4517 try {
4518 #ifdef Z3DEBUG
4519 polynomial_ref orig_u(m_wrapper);
4520 polynomial_ref orig_v(m_wrapper);
4521 if (is_debug_enabled("mgcd_check")) {
4522 orig_u = const_cast<polynomial*>(u);
4523 orig_v = const_cast<polynomial*>(v);
4524 }
4525 #endif
4526
4527 mod_gcd(u, v, u_var_degrees, v_var_degrees, r);
4528
4529 #ifdef Z3DEBUG
4530 if (is_debug_enabled("mgcd_check")) {
4531 polynomial_ref r2(m_wrapper);
4532 flet<bool> use_prs(m_use_prs_gcd, false);
4533 gcd_prs(orig_u, orig_v, x, r2);
4534 CTRACE("mgcd_bug", !eq(r, r2), tout << "u: " << orig_u << "\nv: " << orig_v << "\nr1: " << r << "\nr2: " << r2 << "\n";);
4535 SASSERT(eq(r, r2));
4536 }
4537 #endif
4538 }
4539 catch (const sparse_mgcd_failed &) {
4540 flet<bool> use_prs(m_use_prs_gcd, false);
4541 gcd_prs(u, v, x, r);
4542 }
4543 }
4544 }
4545 else {
4546 gcd_prs(u, v, x, r);
4547 }
4548 }
4549
derivativepolynomial::manager::imp4550 monomial * derivative(monomial const * m, var x) {
4551 return mm().derivative(m, x);
4552 }
4553
derivativepolynomial::manager::imp4554 polynomial * derivative(polynomial const * p, var x) {
4555 SASSERT(is_valid(x));
4556 SASSERT(m_cheap_som_buffer.empty());
4557 unsigned sz = p->size();
4558 for (unsigned i = 0; i < sz; i++) {
4559 monomial * m = p->m(i);
4560 unsigned d = m->degree_of(x);
4561 TRACE("polynomial", m->display(tout); tout << " degree_of x" << x << ": " << d << "\n";);
4562 if (d > 0) {
4563 scoped_numeral n(m_manager);
4564 m_manager.set(n, d);
4565 scoped_numeral a(m_manager);
4566 m_manager.mul(p->a(i), n, a);
4567 m_cheap_som_buffer.add_reset(a, derivative(m, x));
4568 }
4569 }
4570 return m_cheap_som_buffer.mk();
4571 }
4572
square_freepolynomial::manager::imp4573 void square_free(polynomial const * p, polynomial_ref & r) {
4574 if (is_zero(p)) {
4575 r = mk_zero();
4576 return;
4577 }
4578 if (is_const(p)) {
4579 r = const_cast<polynomial*>(p);
4580 return;
4581 }
4582
4583 var x = max_var(p);
4584 scoped_numeral i(m_manager);
4585 polynomial_ref c(pm()), pp(pm());
4586 iccp(p, x, i, c, pp);
4587 polynomial_ref sqf_c(pm());
4588 square_free(c, sqf_c);
4589
4590 polynomial_ref pp_prime(pm());
4591 pp_prime = derivative(pp, x);
4592 polynomial_ref g(pm());
4593 gcd(pp, pp_prime, g);
4594 if (is_const(g)) {
4595 if (eq(sqf_c, c)) {
4596 r = const_cast<polynomial*>(p);
4597 return;
4598 }
4599 }
4600 else {
4601 pp = exact_div(pp, g);
4602 }
4603 r = mul(i, sqf_c);
4604 r = mul(r, pp);
4605 }
4606
is_square_freepolynomial::manager::imp4607 bool is_square_free(polynomial const * p) {
4608 polynomial_ref r(pm());
4609 square_free(p, r);
4610 SASSERT(!eq(p, r) || p == r.get()); // this is a property of the square_free procedure
4611 return p == r.get();
4612 }
4613
square_freepolynomial::manager::imp4614 void square_free(polynomial const * p, var x, polynomial_ref & r) {
4615 if (is_zero(p)) {
4616 r = mk_zero();
4617 return;
4618 }
4619 if (is_const(p)) {
4620 r = const_cast<polynomial*>(p);
4621 return;
4622 }
4623
4624 polynomial_ref p_prime(pm());
4625 p_prime = derivative(p, x);
4626 polynomial_ref g(pm());
4627 gcd(p, p_prime, g);
4628 if (is_const(g)) {
4629 r = const_cast<polynomial*>(p);
4630 }
4631 else {
4632 r = exact_div(p, g);
4633 }
4634 }
4635
is_square_freepolynomial::manager::imp4636 bool is_square_free(polynomial const * p, var x) {
4637 polynomial_ref r(pm());
4638 square_free(p, x, r);
4639 SASSERT(!eq(p, r) || p == r.get()); // this is a property of the square_free procedure
4640 return p == r.get();
4641 }
4642
pwpolynomial::manager::imp4643 void pw(polynomial const * p, unsigned k, polynomial_ref & r) {
4644 if (k == 0) {
4645 r = mk_one();
4646 return;
4647 }
4648 if (k == 1) {
4649 r = const_cast<polynomial*>(p);
4650 return;
4651 }
4652 polynomial_ref result(pm());
4653 result = const_cast<polynomial*>(p);
4654 for (unsigned i = 1; i < k; i++)
4655 result = mul(result, const_cast<polynomial*>(p));
4656 r = result;
4657 #if 0
4658 SASSERT(k >= 2);
4659 unsigned mask = 1;
4660 polynomial_ref p2(pm());
4661 p2 = const_cast<polynomial*>(p);
4662 r = mk_one();
4663 while (true) {
4664 if (mask & k)
4665 r = mul(r, p2);
4666 mask = mask << 1;
4667 if (mask > k)
4668 return;
4669 p2 = mul(p2, p2);
4670 }
4671 #endif
4672 }
4673
eqpolynomial::manager::imp4674 bool eq(polynomial const * p1, polynomial const * p2) {
4675 if (p1 == p2)
4676 return true;
4677 unsigned sz1 = p1->size();
4678 unsigned sz2 = p2->size();
4679 if (sz1 != sz2)
4680 return false;
4681 if (sz1 == 0)
4682 return true;
4683 if (max_var(p1) != max_var(p2))
4684 return false;
4685 m_m2pos.set(p1);
4686 for (unsigned i = 0; i < sz2; i++) {
4687 unsigned pos1 = m_m2pos.get(p2->m(i));
4688 if (pos1 == UINT_MAX || !m_manager.eq(p1->a(pos1), p2->a(i))) {
4689 m_m2pos.reset(p1);
4690 return false;
4691 }
4692 }
4693 m_m2pos.reset(p1);
4694 return true;
4695 }
4696
compose_1_div_xpolynomial::manager::imp4697 polynomial * compose_1_div_x(polynomial const * p) {
4698 SASSERT(is_univariate(p));
4699 if (is_const(p))
4700 return const_cast<polynomial*>(p);
4701 SASSERT(m_cheap_som_buffer.empty());
4702 var x = max_var(p);
4703 unsigned n = degree(p, x);
4704 unsigned sz = p->size();
4705 for (unsigned i = 0; i < sz; i++) {
4706 monomial * m = p->m(i);
4707 SASSERT(m->size() <= 1);
4708 monomial * new_m = mk_monomial(x, n - m->degree_of(x));
4709 m_cheap_som_buffer.add(p->a(i), new_m);
4710 }
4711 return m_cheap_som_buffer.mk();
4712 }
4713
push_powerpolynomial::manager::imp4714 void push_power(sbuffer<power> & pws, var x, unsigned d) {
4715 if (d > 0)
4716 pws.push_back(power(x, d));
4717 }
4718
compose_x_div_ypolynomial::manager::imp4719 polynomial * compose_x_div_y(polynomial const * p, var y) {
4720 SASSERT(is_univariate(p));
4721 SASSERT(max_var(p) != y);
4722 if (is_const(p))
4723 return const_cast<polynomial*>(p);
4724 SASSERT(m_cheap_som_buffer.empty());
4725 var x = max_var(p);
4726 unsigned n = degree(p, x);
4727 unsigned sz = p->size();
4728 sbuffer<power> pws;
4729 for (unsigned i = 0; i < sz; i++) {
4730 unsigned k = p->m(i)->degree_of(x);
4731 pws.reset();
4732 if (x < y) {
4733 push_power(pws, x, k);
4734 push_power(pws, y, n - k);
4735 }
4736 else {
4737 push_power(pws, y, n - k);
4738 push_power(pws, x, k);
4739 }
4740 monomial * new_m = mk_monomial(pws.size(), pws.data());
4741 m_cheap_som_buffer.add(p->a(i), new_m);
4742 }
4743 return m_cheap_som_buffer.mk();
4744 }
4745
compose_ypolynomial::manager::imp4746 polynomial * compose_y(polynomial const * p, var y) {
4747 SASSERT(is_valid(y));
4748 SASSERT(is_univariate(p));
4749 if (y == max_var(p) || is_const(p))
4750 return const_cast<polynomial*>(p);
4751 SASSERT(m_cheap_som_buffer.empty());
4752 unsigned sz = p->size();
4753 for (unsigned i = 0; i < sz; i++) {
4754 monomial * m = p->m(i);
4755 SASSERT(m->size() <= 1);
4756 monomial * new_m;
4757 if (m->size() == 0)
4758 new_m = m;
4759 else
4760 new_m = mk_monomial(y, m->degree(0));
4761 m_cheap_som_buffer.add(p->a(i), new_m);
4762 }
4763 return m_cheap_som_buffer.mk();
4764 }
4765
compose_minus_xpolynomial::manager::imp4766 polynomial * compose_minus_x(polynomial const * p) {
4767 SASSERT(is_univariate(p));
4768 if (is_const(p))
4769 return const_cast<polynomial*>(p);
4770 SASSERT(m_cheap_som_buffer.empty());
4771 scoped_numeral a(m_manager);
4772 unsigned sz = p->size();
4773 for (unsigned i = 0; i < sz; i++) {
4774 monomial * m = p->m(i);
4775 if (m->total_degree() % 2 == 0) {
4776 m_cheap_som_buffer.add(p->a(i), p->m(i));
4777 }
4778 else {
4779 m_manager.set(a, p->a(i));
4780 m_manager.neg(a);
4781 m_cheap_som_buffer.add(a, p->m(i));
4782 }
4783 }
4784 return m_cheap_som_buffer.mk();
4785 }
4786
4787 /**
4788 \brief Store the positions (in m_degree2pos) of the monomials of an univariate polynomial.
4789 */
save_degree2pospolynomial::manager::imp4790 void save_degree2pos(polynomial const * p) {
4791 SASSERT(is_univariate(p));
4792 var x = max_var(p);
4793 unsigned n = degree(p, x);
4794 m_degree2pos.reserve(n+1, UINT_MAX);
4795 unsigned sz = p->size();
4796 for (unsigned i = 0; i < sz; i++) {
4797 monomial * m = p->m(i);
4798 SASSERT(m->size() <= 1);
4799 SASSERT(m_degree2pos[m->total_degree()] == UINT_MAX);
4800 m_degree2pos[m->total_degree()] = i;
4801 }
4802 }
4803
4804 /**
4805 \brief Undo the modifications in m_degree2pos performed by save_degree2pos.
4806 */
reset_degree2pospolynomial::manager::imp4807 void reset_degree2pos(polynomial const * p) {
4808 SASSERT(is_univariate(p));
4809 unsigned sz = p->size();
4810 for (unsigned i = 0; i < sz; i++) {
4811 monomial * m = p->m(i);
4812 SASSERT(m->size() <= 1);
4813 SASSERT(m_degree2pos[m->total_degree()] == i);
4814 m_degree2pos[m->total_degree()] = UINT_MAX;
4815 }
4816 }
4817
4818 // muladd may throw if the cancel flag is set.
4819 // So we wrap the degree2pos set and reset
4820 // in a scoped class to ensure the state is clean
4821 // on exit.
4822 struct scoped_degree2pos {
4823 imp& pm;
4824 polynomial const* p;
scoped_degree2pospolynomial::manager::imp::scoped_degree2pos4825 scoped_degree2pos(imp& pm, polynomial const* p):
4826 pm(pm),
4827 p(p)
4828 {
4829 pm.save_degree2pos(p);
4830 }
4831
~scoped_degree2pospolynomial::manager::imp::scoped_degree2pos4832 ~scoped_degree2pos() {
4833 pm.reset_degree2pos(p);
4834 }
4835 };
4836
4837 /**
4838 \brief Given an univariate polynomial p(x) and a polynomial q(y_1, ..., y_n),
4839 return a polynomial r(y_1, ..., y_n) = p(q(y_1, ..., y_n)).
4840 */
composepolynomial::manager::imp4841 void compose(polynomial const * p, polynomial const * q, polynomial_ref & r) {
4842 SASSERT(is_univariate(p));
4843 if (is_const(p)) {
4844 r = const_cast<polynomial*>(p);
4845 return;
4846 }
4847 var x = max_var(p);
4848 unsigned d = degree(p, x);
4849 scoped_degree2pos _sd2pos(*this, p);
4850 scoped_numeral a(m());
4851 m_manager.set(a, p->a(m_degree2pos[d]));
4852 r = mk_const(a);
4853 for (unsigned i = 1; i <= d; i++) {
4854 unsigned pos = m_degree2pos[d-i];
4855 if (pos != UINT_MAX)
4856 m_manager.set(a, p->a(pos));
4857 else
4858 m_manager.reset(a);
4859 r = muladd(q, r, a);
4860 }
4861 }
4862
mk_x_minus_ypolynomial::manager::imp4863 polynomial * mk_x_minus_y(var x, var y) {
4864 numeral zero(0);
4865 numeral minus_one; // It is not safe to initialize with -1 when numeral_manager is GF_2
4866 m_manager.set(minus_one, -1);
4867 numeral as[2] = { numeral(1), std::move(minus_one) };
4868 var xs[2] = { x, y };
4869 return mk_linear(2, as, xs, zero);
4870 }
4871
compose_x_minus_ypolynomial::manager::imp4872 void compose_x_minus_y(polynomial const * p, var y, polynomial_ref & r) {
4873 SASSERT(is_valid(y));
4874 SASSERT(is_univariate(p));
4875 var x = max_var(p);
4876 if (y == max_var(p)) {
4877 r = coeff(p, x, 0);
4878 return;
4879 }
4880 polynomial_ref x_minus_y(pm());
4881 x_minus_y = mk_x_minus_y(x, y);
4882 return compose(p, x_minus_y, r);
4883 }
4884
mk_x_plus_ypolynomial::manager::imp4885 polynomial * mk_x_plus_y(var x, var y) {
4886 numeral zero(0);
4887 numeral as[2] = { numeral(1), numeral(1) };
4888 var xs[2] = { x, y };
4889 return mk_linear(2, as, xs, zero);
4890 }
4891
compose_x_plus_ypolynomial::manager::imp4892 void compose_x_plus_y(polynomial const * p, var y, polynomial_ref & r) {
4893 SASSERT(is_valid(y));
4894 SASSERT(is_univariate(p));
4895 var x = max_var(p);
4896 polynomial_ref x_plus_y(pm());
4897 x_plus_y = mk_x_plus_y(x, y);
4898 return compose(p, x_plus_y, r);
4899 }
4900
4901 // Return the polynomial x - c
mk_x_minus_cpolynomial::manager::imp4902 polynomial * mk_x_minus_c(var x, numeral const & c) {
4903 numeral as[2];
4904 m_manager.set(as[0], c);
4905 m_manager.set(as[1], 1);
4906 m_manager.neg(as[0]);
4907 polynomial * p = mk_univariate(x, 1, as);
4908 TRACE("polynomial", tout << "x - c: "; p->display(tout, m_manager); tout << "\n";);
4909 m_manager.del(as[0]);
4910 m_manager.del(as[1]);
4911 return p;
4912 }
4913
compose_x_minus_cpolynomial::manager::imp4914 void compose_x_minus_c(polynomial const * p, numeral const & c, polynomial_ref & r) {
4915 SASSERT(is_univariate(p));
4916 if (m_manager.is_zero(c)) {
4917 r = const_cast<polynomial*>(p);
4918 return;
4919 }
4920 var x = max_var(p);
4921 polynomial_ref x_minus_c(pm());
4922 x_minus_c = mk_x_minus_c(x, c);
4923 return compose(p, x_minus_c, r);
4924 }
4925
4926 /**
4927 \brief Template for computing several variations of pseudo division algorithm.
4928 If degree(p) < degree(q) --> Q = m_zero, d = 0, R = p
4929
4930 The following property is guaranteed by this method
4931
4932 l(q)^d * p = Q * q + R
4933
4934 where l(q) is coeff(q, x, degree(q, x))
4935
4936 Possible configurations:
4937 Exact_d == true, then d = degree(p) - degree(q) + 1.
4938 If Exact_d == false, then d <= degree(p) - degree(q) + 1.
4939
4940 If Quotient == false, Q is not computed.
4941
4942 If ModD == true, then x2d must be different from 0.
4943 Moreover, p and q are assumed to be normalized modulo x2d.
4944 For all x, x2d->degree(x) > 0 implies degree(p, x) < x2d->degree(x) and degree(q, x) < x2d->degree(x)
4945 Moreover, the division algorithm will compute Q and R modulo x2d.
4946 */
4947 template<bool Exact_d, bool Quotient, bool ModD>
pseudo_division_corepolynomial::manager::imp4948 void pseudo_division_core(polynomial const * p, polynomial const * q, var x, unsigned & d, polynomial_ref & Q, polynomial_ref & R,
4949 var2degree const * x2d = nullptr) {
4950 SASSERT(is_valid(x));
4951 SASSERT(!ModD || x2d != 0);
4952 TRACE("polynomial", tout << "pseudo_division\np: "; p->display(tout, m_manager);
4953 tout << "\nq: "; q->display(tout, m_manager); tout << "\nx: " << x << "\n";);
4954 polynomial * A = const_cast<polynomial*>(p);
4955 polynomial * B = const_cast<polynomial*>(q);
4956 unsigned deg_A = degree(A, x);
4957 unsigned deg_B = degree(B, x);
4958 if (deg_B == 0) {
4959 R = m_zero;
4960 if (Quotient) {
4961 if (Exact_d) {
4962 d = deg_A /* - deg_B */ + 1;
4963 // Since degree(B) = 0, lc(B) == B
4964 // lc(B)^d * A = Q * B + R --> (using R == 0, lc(B) == B)
4965 // B^d * A = Q * B
4966 // Thus, Q = A * B^{d-1}
4967 if (d == 1) {
4968 Q = A;
4969 return;
4970 }
4971 polynomial_ref Bdm1(pm());
4972 pw(B, d - 1, Bdm1);
4973 Q = mul(A, Bdm1);
4974 if (ModD)
4975 Q = mod_d(Q, *x2d);
4976 }
4977 else {
4978 d = 1;
4979 Q = A;
4980 }
4981 }
4982 return;
4983 }
4984 if (deg_B > deg_A) {
4985 Q = m_zero;
4986 R = A;
4987 d = 0;
4988 }
4989 scoped_numeral minus_a(m_manager);
4990 polynomial_ref l_B(pm()); // leading coefficient of B (that is, coefficient of x^(deg_B))
4991 polynomial_ref r_B(pm()); // reduct of B (that is, B without leading coefficient)
4992 l_B = coeff(B, x, deg_B, r_B);
4993 d = 0;
4994 R = A;
4995 Q = m_zero;
4996 while (true) {
4997 checkpoint();
4998 TRACE("polynomial",
4999 tout << "A: "; A->display(tout, m_manager); tout << "\n";
5000 tout << "B: "; B->display(tout, m_manager); tout << "\n";
5001 tout << "l_B: "; l_B->display(tout, m_manager); tout << "\n";
5002 tout << "r_B: "; r_B->display(tout, m_manager); tout << "\n";
5003 tout << "Q: "; Q->display(tout, m_manager); tout << "\n";
5004 tout << "R: "; R->display(tout, m_manager); tout << "\n";
5005 tout << "d: " << d << "\n";);
5006 unsigned deg_R = degree(R, x);
5007 if (deg_B > deg_R) {
5008 if (Exact_d) {
5009 // Adjust Q and R
5010 unsigned exact_d = deg_A - deg_B + 1;
5011 SASSERT(d <= exact_d);
5012 if (d < exact_d) {
5013 unsigned e = exact_d - d;
5014 polynomial_ref l_B_e(pm());
5015 pw(l_B, e, l_B_e);
5016 TRACE("polynomial", tout << "l_B_e: " << l_B_e << "\n";);
5017 if (Quotient) {
5018 Q = mul(l_B_e, Q);
5019 if (ModD)
5020 Q = mod_d(Q, *x2d);
5021 }
5022 R = mul(l_B_e, R);
5023 if (ModD)
5024 R = mod_d(R, *x2d);
5025 }
5026 }
5027 return;
5028 }
5029 // S <- l_R * x^(deg_R - deg_B)
5030 // R <- l_B * R - S * B
5031 // Note that the goal is to cancel x^deg_R in R.
5032 // m_som_buffer will contain the new monomials of R.
5033 m_som_buffer.reset();
5034 // We can accomplish that by traversing the current R, and
5035 // - For each monomial a * m * x^deg_R --> m_som_buffer.addmul(-a, m * x^(deg_R - deg_B), r_B)
5036 // Note that m*x^(deg_R - deg_B) is div_x_k(m*x^deg_R, deg_B)
5037 // - For other monomials a*m, ---> m_som_buffer.addmul(a, m, l_B)
5038 // Note that, with this trick and don't need to create the temporary polynomials l_B * R and S * B
5039 //
5040 // If the quotient needs to be computed, we have that
5041 // S <- l_R * x^(deg_R - deg_B)
5042 // Q <- l_B * Q + S
5043 // The new monomials of Q are stored in m_som_buffer2
5044 // When traversing R, for each monomial a*m*x^deg_R we add m_som_buffer2.add(a, m*x^(deg_R - deg_B))
5045 m_som_buffer2.reset();
5046 //
5047 unsigned sz = R->size();
5048 for (unsigned i = 0; i < sz; i++) {
5049 monomial * m = R->m(i);
5050 numeral const & a = R->a(i);
5051 if (m->degree_of(x) == deg_R) {
5052 monomial_ref m_prime(pm());
5053 m_prime = div_x_k(m, x, deg_B);
5054 CTRACE("polynomial", m->degree_of(x) != deg_R - deg_B,
5055 tout << "deg_R: " << deg_R << ", deg_B: " << deg_B << ", x: " << x << "\n";
5056 m->display(tout); tout << ", "; m_prime->display(tout); tout << "\n";);
5057 SASSERT(m->degree_of(x) == deg_R);
5058 SASSERT(m_prime->degree_of(x) == deg_R - deg_B);
5059 if (Quotient) {
5060 m_som_buffer2.add(a, m_prime);
5061 }
5062 m_manager.set(minus_a, a);
5063 m_manager.neg(minus_a);
5064 m_som_buffer.addmul(minus_a, m_prime, r_B);
5065 }
5066 else {
5067 m_som_buffer.addmul(a, m, l_B);
5068 }
5069 }
5070 if (ModD)
5071 m_som_buffer.mod_d(*x2d);
5072 R = m_som_buffer.mk();
5073 if (Quotient) {
5074 // m_som_buffer2 contains new monomials of Q <- l_B Q + S
5075 // We have already copied S to m_som_buffer2.
5076 // To add l_B * Q, we just traverse Q executing addmul(Q->a(i), Q->m(i), l_B)
5077 unsigned sz = Q->size();
5078 for (unsigned i = 0; i < sz; i++) {
5079 m_som_buffer2.addmul(Q->a(i), Q->m(i), l_B);
5080 }
5081 if (ModD)
5082 m_som_buffer2.mod_d(*x2d);
5083 Q = m_som_buffer2.mk();
5084 }
5085 d++;
5086 }
5087 }
5088
exact_pseudo_remainderpolynomial::manager::imp5089 void exact_pseudo_remainder(polynomial const * p, polynomial const * q, var x, polynomial_ref & R) {
5090 unsigned d;
5091 polynomial_ref Q(pm());
5092 pseudo_division_core<true, false, false>(p, q, x, d, Q, R);
5093 }
5094
pseudo_remainderpolynomial::manager::imp5095 void pseudo_remainder(polynomial const * p, polynomial const * q, var x, unsigned & d, polynomial_ref & R) {
5096 #ifdef Z3DEBUG
5097 polynomial_ref old_p(pm());
5098 polynomial_ref old_q(pm());
5099 old_p = const_cast<polynomial*>(p); // R may be aliasing p and q
5100 old_q = const_cast<polynomial*>(q);
5101 polynomial_ref Q(pm());
5102 pseudo_division_core<false, true, false>(p, q, x, d, Q, R);
5103 // debugging code
5104 // check if lc(q)^d * p = Q * q + R
5105 polynomial_ref l(pm());
5106 l = coeff(old_q, x, degree(q, x));
5107 polynomial_ref ld(pm());
5108 pw(l, d, ld);
5109 polynomial_ref lhs(pm());
5110 lhs = mul(ld, old_p);
5111 polynomial_ref rhs(pm());
5112 rhs = mul(Q, old_q);
5113 rhs = add(rhs, R);
5114 bool is_eq = eq(lhs, rhs);
5115 TRACE("pseudo_remainder",
5116 tout << "pseudo_division bug\n";
5117 tout << "p: "; old_p->display(tout, m_manager); tout << "\n";
5118 tout << "q: "; old_q->display(tout, m_manager); tout << "\n";
5119 tout << "Q: " << Q << "\nR: " << R << "\n";
5120 tout << "l^d: " << ld << "\nlhs: " << lhs << "\nrhs: " << rhs << "\n";);
5121 SASSERT(is_eq);
5122 #else
5123 polynomial_ref Q(pm());
5124 pseudo_division_core<false, false, false>(p, q, x, d, Q, R);
5125 #endif
5126 }
5127
exact_pseudo_divisionpolynomial::manager::imp5128 void exact_pseudo_division(polynomial const * p, polynomial const * q, var x, polynomial_ref & Q, polynomial_ref & R) {
5129 unsigned d;
5130 pseudo_division_core<true, true, false>(p, q, x, d, Q, R);
5131 }
5132
pseudo_divisionpolynomial::manager::imp5133 void pseudo_division(polynomial const * p, polynomial const * q, var x, unsigned & d, polynomial_ref & Q, polynomial_ref & R) {
5134 pseudo_division_core<false, true, false>(p, q, x, d, Q, R);
5135 }
5136
exact_divpolynomial::manager::imp5137 polynomial * exact_div(polynomial const * p, numeral const & c) {
5138 SASSERT(!m().is_zero(c));
5139
5140 som_buffer & R = m_som_buffer;
5141 R.reset();
5142
5143 numeral tmp;
5144
5145 unsigned sz = p->size();
5146 for (unsigned i = 0; i < sz; ++ i) {
5147 SASSERT(m().divides(c, p->a(i)));
5148 m().div(p->a(i), c, tmp);
5149 if (!m().is_zero(tmp)) {
5150 R.add(tmp, p->m(i));
5151 }
5152 }
5153
5154 m().del(tmp);
5155
5156 return R.mk();
5157 }
5158
exact_divpolynomial::manager::imp5159 polynomial * exact_div(polynomial const * p, polynomial const * q) {
5160 TRACE("polynomial", tout << "exact division\np: "; p->display(tout, m_manager); tout << "\nq: "; q->display(tout, m_manager); tout << "\n";);
5161 if (is_zero(p))
5162 return const_cast<polynomial*>(p);
5163 SASSERT(!is_zero(q));
5164 m_som_buffer.reset();
5165 m_som_buffer2.reset();
5166 som_buffer & R = m_som_buffer;
5167 som_buffer & C = m_som_buffer2;
5168 R.add(p);
5169 unsigned max_q = q->graded_lex_max_pos();
5170 monomial * m_q = q->m(max_q);
5171 numeral const & a_q = q->a(max_q);
5172 monomial_ref m_r_q_ref(pm());
5173 scoped_numeral a_r_q(m_manager);
5174 while (true) {
5175 checkpoint();
5176 unsigned max_R = R.graded_lex_max_pos();
5177 if (max_R == UINT_MAX) {
5178 // R is empty
5179 R.reset();
5180 return C.mk();
5181 }
5182 monomial const * m_r = R.m(max_R);
5183 numeral const & a_r = R.a(max_R);
5184 monomial_ref m_r_q(pm());
5185 VERIFY(div(m_r, m_q, m_r_q));
5186 TRACE("polynomial", tout << "m_r: "; m_r->display(tout); tout << "\nm_q: "; m_q->display(tout); tout << "\n";
5187 if (m_r_q) { tout << "m_r_q: "; m_r_q->display(tout); tout << "\n"; });
5188 m_r_q_ref = m_r_q;
5189 m_manager.div(a_r, a_q, a_r_q);
5190 C.add(a_r_q, m_r_q); // C <- C + (a_r/a_q)*(m_r/m_q)
5191 m_manager.neg(a_r_q);
5192 R.addmul(a_r_q, m_r_q, q); // R <- R - (a_r/a_q)*(m_r/m_q)*Q
5193 }
5194 }
5195
5196 // Return true if q divides p.
dividespolynomial::manager::imp5197 bool divides(polynomial const * q, polynomial const * p) {
5198 TRACE("polynomial", tout << "divides\nq: "; q->display(tout, m_manager); tout << "\np: "; p->display(tout, m_manager); tout << "\n";);
5199 TRACE("divides", tout << "divides\nq: "; q->display(tout, m_manager); tout << "\np: "; p->display(tout, m_manager); tout << "\n";);
5200 if (is_zero(p))
5201 return true;
5202 SASSERT(!is_zero(q));
5203 m_som_buffer.reset();
5204 m_som_buffer2.reset();
5205 som_buffer & R = m_som_buffer;
5206 R.add(p);
5207 unsigned max_q = q->graded_lex_max_pos();
5208 monomial * m_q = q->m(max_q);
5209 numeral const & a_q = q->a(max_q);
5210 monomial_ref m_r_q_ref(pm());
5211 scoped_numeral a_r_q(m_manager);
5212 while (true) {
5213 checkpoint();
5214 unsigned max_R = R.graded_lex_max_pos();
5215 if (max_R == UINT_MAX) {
5216 // R is empty
5217 return true;
5218 }
5219 monomial const * m_r = R.m(max_R);
5220 numeral const & a_r = R.a(max_R);
5221 monomial_ref m_r_q(pm());
5222 bool q_div_r = div(m_r, m_q, m_r_q);
5223 m_r_q_ref = m_r_q;
5224 TRACE("polynomial", tout << "m_r: "; m_r->display(tout); tout << "\nm_q: "; m_q->display(tout); tout << "\n";
5225 if (m_r_q) { tout << "m_r_q: "; m_r_q->display(tout); tout << "\n"; });
5226 if (!q_div_r)
5227 return false;
5228 if (!m_manager.divides(a_q, a_r))
5229 return false;
5230 m_manager.div(a_r, a_q, a_r_q);
5231 m_manager.neg(a_r_q);
5232 R.addmul(a_r_q, m_r_q, q); // R <- R - (a_r/a_q)*(m_r/m_q)*Q
5233 }
5234 }
5235
quasi_resultantpolynomial::manager::imp5236 void quasi_resultant(polynomial const * p, polynomial const * q, var x, polynomial_ref & r) {
5237 // For h_0 = p and h_1 = q, we compute the following sequence
5238 // using the pseudo remainder procedure
5239 //
5240 // l(h_1)^d_1 * h_0 = q_1 * h_1 + h_2
5241 // l(h_2)^d_2 * h_1 = q_2 * h_2 + h_3
5242 // l(h_3)^d_3 * h_2 = q_3 * h_3 + h_4
5243 // ...
5244 // l(h_n)^d_n * h_{n-1} = q_n * h_n + h_{n+1}
5245 //
5246 // where
5247 // degree(h_i, x) > 0 for all i in [0, n], and
5248 // degree(h_{n+1}, x) == 0
5249 //
5250 // l(h_i) is the leading coefficient of h_i with respect to variable x.
5251 // l(h_i) is in general a polynomial.
5252 // For example, l( y*x^2 + x^2 + y^2 x + 1 ) = y + 1
5253 //
5254 // d^i is an unsigned integer. It is introduce by the pseudo remainder procedure
5255 // because the coefficients of x^k do not form a field. That is, they are elements of a polynomial ring Q[y_1, ..., y_n].
5256 // Their values are irrelevant for the correctness of this procedure.
5257 //
5258 // Observation 1:
5259 // If h_0 and h_1 are polynomials in Q[y_1, ..., y_n, x],
5260 // then h_{n+1} is a polynomial in Q[y_1, ..., y_n].
5261 //
5262 // Observation 2:
5263 // For any (complex values) a_1, ..., a_n, b,
5264 // if h_0(a_1, ..., a_n, b) = h_1(a_1, ..., a_n, b) = 0
5265 // then for any h_i in the sequence, h_i(a_1, ..., a_n, b) = 0.
5266 // In particular, h_{n+1}(a_1, ..., a_n) = 0
5267 //
5268 // Observation 3:
5269 // The procedure terminates because degree(h_i, x) > degree(h_{i+1}, x)
5270 // for all i >= 1
5271 //
5272 // Observation 4:
5273 // If h_{n+1} is the zero polynomial, then
5274 // For any complex numbers a_1, ..., a_n
5275 // the univariate polynomials p(a_1, ..., a_n, x) and q(a_1, ..., a_n, x) in C[x]
5276 // have a common root.
5277 // Reason:
5278 // If h_n(a_1, ..., a_n, x) is not the zero polynomial, then it is the GCD of p(a_1, ..., a_n, x) and q(a_1, ..., a_n, x),
5279 // and it contains the common roots.
5280 // If h_n(a_1, ..., a_n, x) is the zero polynomial, then
5281 // we consider h_{n-1}(a_1, ..., a_n, x). If it is not the zero polynomial then it is the GCD and we are done,
5282 // otherwise we consider h_{n-2}(a_1, ..., a_n, x), and we continue the same process.
5283 // Thus, eventually we find a h_i(a_1, ..., a_n, x) for i > 1 that is the GCD, or q(a_1, ..., a_n, x) is the zero polynomial,
5284 // and any polynomial p(a_1, ..., a_n, x) has a common root with the zero polynomial.
5285 //
5286 SASSERT(degree(p, x) > 0);
5287 SASSERT(degree(q, x) > 0);
5288 polynomial_ref h_0(pm());
5289 polynomial_ref h_1(pm());
5290 polynomial_ref h_2(pm());
5291 if (degree(p, x) < degree(q, x))
5292 std::swap(p, q);
5293 h_0 = const_cast<polynomial*>(p);
5294 h_1 = const_cast<polynomial*>(q);
5295 unsigned d;
5296 while (true) {
5297 SASSERT(degree(h_1, x) <= degree(h_0, x));
5298 pseudo_remainder(h_0, h_1, x, d, h_2);
5299 TRACE("polynomial", tout << "h_0: " << h_0 << "\nh_1: " << h_1 << "\nh_2: " << h_2 << "\n";);
5300 SASSERT(degree(h_2, x) < degree(h_1, x));
5301 // We have that
5302 // l(h_1)^d h_0 = Q h_1 + h_2.
5303 // Q is the quotient of the division.
5304 // l(h_1) is the leading coefficient of h_1.
5305 // From this equation, we have that any zero of h_0 and h_1 is also a zero of h_2
5306 if (degree(h_2, x) == 0) {
5307 r = h_2;
5308 return;
5309 }
5310 h_0 = h_1;
5311 h_1 = h_2;
5312 // this computation will terminate since the pseudo_remainder guarantees that
5313 // degree(h_2, x) < degree(h_1, x)
5314 }
5315 }
5316
5317 // sign = sign * (-1)^(deg_A * deg_B)
update_signpolynomial::manager::imp5318 static void update_sign(unsigned deg_A, unsigned deg_B, bool & sign) {
5319 if (deg_A % 2 == 0 || deg_B % 2 == 0)
5320 return;
5321 sign = !sign;
5322 }
5323
5324 /**
5325 \brief Compute the resultant of p and q with respect to r.
5326
5327 The Resultant is usually defined as the determinant of the
5328 Sylvester matrix for p and q. This matrix is built using
5329 the coefficients of p and q with respect to x.
5330
5331 Given p and q polynomials in Q[y_1, ..., y_n, x], r is a polynomial in Q[y_1, ..., y_n].
5332
5333 Property 1)
5334 If p and q can be written as
5335 p = a_m * (x - alpha_1) * ... * (x - alpha_m)
5336 q = b_n * (x - beta_1) * ... * (x - beta_n)
5337 Then,
5338 Res(p, q, x) = a_m^n * b_n^m * Product_{i in [1,m], j in [1, n]} (alpha_i - beta_j)
5339
5340 Remark: if p and q are univariate polynomials, then alpha_i's and beta_j's are the roots
5341 of p and q, then Res(p, q, x) is the product of the differences of their roots modulo
5342 a constant.
5343
5344 Property 2)
5345 For any (complex values) a_1, ..., a_n, b,
5346 if p(a_1, ..., a_n, b) = q(a_1, ..., a_n, b) = 0, then r(a_1, ..., b_n) = 0
5347
5348 Property 3)
5349 There are polynomials U and V in Q[y_1, ..., y_n, x] s.t.
5350 p*U + q*V = r
5351 s.t.
5352 deg(U, x) < deg(q, x)
5353 and
5354 deg(V, x) < deg(p, x)
5355
5356 We use Res(p, q, x) to denote the resultant of p and q.
5357
5358 Property 4) (follows from 1)
5359 If Res(p, q, x) = 0, then p and q have a common divisor.
5360
5361 Resultant Calculus:
5362 Let A and B be two polynomials of degree m and n on variable x.
5363 Let c and d be numerals.
5364
5365 Res(A, B, x) = (-1)^(m*n) * Res(B, A, x)
5366 Res(cA, B, x) = c^n * Res(A, B, x)
5367 Res(c, B, x) = c^n
5368 Res(0, B, x) = 0 if n > 0
5369 Res(c, d, x) = 1
5370 Res(A, B1*B2, x) = Res(A, B1, x)*Res(A, B2, x)
5371 Res(A, A*Q + B, x) = coeff(A, x)^(l-n) * Res(A, B, x) where l = deg(A*Q + R)
5372
5373 The last equation suggests an approach for computing the Resultant using
5374 pseudo-division instead of determinants.
5375
5376 Given A and B s.t. degree(A, x) = m >= n = degree(B, x)
5377 Then lc^d * A = Q * B + R
5378 where lc = coeff(B, x), and
5379 pseudo_division(A, B, x, d, Q, R);
5380 r = degree(R)
5381 Then we have:
5382 (lc^d)^n * Res(A, B) = Res(A * l^d, B) = Res(Q * B + R, B) = (-1)^(m*n) * Res(B, Q * B + R) = (-1)^(m*n) * lc^(m-r) * Res(B, R)
5383
5384 So,
5385 lc^(d*n) * Res(A, B) = (-1)^(m*n) * lc^(m-r) * Res(B, R)
5386
5387 From the pseudo-division algorithm, we have that:
5388 1) 1 <= d <= m - n + 1
5389 2) 0 <= r < n <= m
5390
5391 d*n >= m >= m-r
5392
5393 So, if d*n == m-r
5394 Res(A, B) = (-1)^(m*n) * Res(R, B)
5395 if d*n > m-r
5396 Res(A, B) = (-1)^(m*n) * exact_div(Res(R, B), lc^(d*n - m + r))
5397 if d*n < m-r
5398 Res(A, B) = (-1)^(m*n) * mul(Res(R, B), lc^(m - r - d * n))
5399 */
resultantpolynomial::manager::imp5400 void resultant(polynomial const * p, polynomial const * q, var x, polynomial_ref & result) {
5401 polynomial_ref A(pm());
5402 polynomial_ref B(pm());
5403 A = const_cast<polynomial*>(p);
5404 B = const_cast<polynomial*>(q);
5405 TRACE("resultant", tout << "resultant(A, B, x)\nA: " << A << "\nB: " << B << "\nx: " << x << "\n";);
5406 // Res(0, B) = Res(A, 0) = 0
5407 if (is_zero(A) || is_zero(B)) {
5408 result = mk_zero();
5409 return;
5410 }
5411 // Res(c, d, x) = 1 c and d are constants
5412 // Res(c, B, x) = c^{deg(B)}
5413 if (is_const(A)) {
5414 if (is_const(B))
5415 result = mk_one();
5416 else
5417 pw(A, degree(B, x), result);
5418 return;
5419 }
5420 // Res(A, d, x) = d^{deg(A)}
5421 if (is_const(B)) {
5422 pw(B, degree(A, x), result);
5423 return;
5424 }
5425
5426 // decompose A and B into
5427 // A = iA*cA*ppA
5428 // B = iB*cB*ppB
5429 scoped_numeral iA(m_manager), iB(m_manager);
5430 polynomial_ref cA(pm()), cB(pm());
5431 polynomial_ref ppA(pm()), ppB(pm());
5432 iccp(A, x, iA, cA, ppA);
5433 iccp(B, x, iB, cB, ppB);
5434 cA = mul(iA, cA);
5435 cB = mul(iB, cB);
5436 // At this point, A = cA*ppA and B = cB*ppB, where cA and cB are the content of A and B, and ppA and ppB the primitive polynomials
5437 polynomial_ref t(pm());
5438 // t <- cA^{deg(B)}*cB^{deg(A)}
5439 pw(cA, degree(B, x), cA);
5440 pw(cB, degree(A, x), cB);
5441 t = mul(cA, cB);
5442 A = ppA;
5443 B = ppB;
5444 //
5445 TRACE("resultant", tout << "resultant(A, B, x) after normalization\nA: " << A << "\nB: " << B << "\nx: " << x << "\n";
5446 tout << "t: " << t << "\n";);
5447
5448 int s = 1;
5449 unsigned degA = degree(A, x);
5450 unsigned degB = degree(B, x);
5451 if (degA < degB) {
5452 A.swap(B);
5453 if (degA % 2 == 1 && degB % 2 == 1)
5454 s = -1;
5455 }
5456
5457 polynomial_ref R(pm());
5458 polynomial_ref g(pm());
5459 polynomial_ref h(pm());
5460 polynomial_ref new_h(pm());
5461 // g <- 1
5462 g = mk_one();
5463 // h <- 1
5464 h = mk_one();
5465
5466 while (true) {
5467 TRACE("resultant", tout << "A: " << A << "\nB: " << B << "\n";);
5468 degA = degree(A, x);
5469 degB = degree(B, x);
5470 SASSERT(degA >= degB);
5471 unsigned delta = degA - degB;
5472 if (degA % 2 == 1 && degB % 2 == 1)
5473 s = -s;
5474 // lc(B)^delta+1 A = BQ + R
5475 exact_pseudo_remainder(A, B, x, R);
5476 A = B;
5477 // B <- R/g*h^{delta}
5478 B = exact_div(R, g);
5479 for (unsigned i = 0; i < delta; i++)
5480 B = exact_div(B, h);
5481 // g <- lc(A)
5482 g = lc(A, x);
5483 // h <- g^delta * h^{1-delta}
5484 new_h = mk_one();
5485 pw(g, delta, new_h);
5486 if (delta > 1) {
5487 for (unsigned i = 0; i < delta - 1; i++)
5488 new_h = exact_div(new_h, h);
5489 }
5490 h = new_h;
5491 if (degree(B, x) == 0) {
5492 unsigned degA = degree(A, x);
5493 // h <- lc(B)^{deg(A)} * h^{1-deg(A)}
5494 new_h = lc(B, x);
5495 pw(new_h, degA, new_h);
5496 if (degA > 1) {
5497 for (unsigned i = 0; i < degA - 1; i++)
5498 new_h = exact_div(new_h, h);
5499 }
5500 h = new_h;
5501 // result <- s*t*h
5502 result = mul(t, h);
5503 if (s < 0)
5504 result = neg(result);
5505 return;
5506 }
5507 }
5508 }
5509
5510 /**
5511 \brief Return the discriminant of p with respect to x.
5512
5513 Disc(p, x) = (-1)^(m * (m-1)/2) * Resultant(p, dp/dx, x) / coeff(p, x)
5514 dp/dx is the derivative of p with respect to x.
5515 */
discriminantpolynomial::manager::imp5516 void discriminant(polynomial const * p, var x, polynomial_ref & r) {
5517 polynomial_ref p_prime(pm());
5518 unsigned m = degree(p, x);
5519 if (m == 0) {
5520 r = m_zero;
5521 return;
5522 }
5523 p_prime = derivative(p, x);
5524 resultant(p, p_prime, x, r);
5525 bool sign = (static_cast<uint64_t>(m) * static_cast<uint64_t>(m-1))%4 != 0;
5526 TRACE("resultant", tout << "discriminant sign: " << sign << "\n";);
5527 scoped_numeral lc(m_manager);
5528 if (const_coeff(p, x, m, lc)) {
5529 if (sign)
5530 m_manager.neg(lc);
5531 r = div(r, lc);
5532 }
5533 else {
5534 if (sign)
5535 r = neg(r);
5536 polynomial_ref c(pm());
5537 c = coeff(p, x, m);
5538 r = exact_div(r, c);
5539 }
5540 }
5541
subresultant_chainpolynomial::manager::imp5542 void subresultant_chain(polynomial const * p, polynomial const * q, var x, polynomial_ref_vector & sRes) {
5543 // REMARK: the code does not work if deg_p == deg_q
5544 unsigned deg_p = degree(p, x);
5545 unsigned deg_q = degree(q, x);
5546 if (deg_p < deg_q) {
5547 std::swap(p, q);
5548 std::swap(deg_p, deg_q);
5549 }
5550 SASSERT(deg_p > 0);
5551 unsigned n = deg_p;
5552 sRes.reset();
5553 sRes.resize(n + 1); // the sequence is from 0 ... n
5554 sRes.set(n, const_cast<polynomial*>(p));
5555 sRes.set(n - 1, const_cast<polynomial*>(q));
5556
5557 polynomial_ref R_j_plus_1(pm());
5558 polynomial_ref prem(pm());
5559 polynomial_ref newS(pm());
5560
5561 unsigned j = n - 1;
5562 while (j > 0) {
5563 SASSERT(degree(sRes.get(j+1), x) == j+1); // sRes_{j+1} is regular
5564 if (j == n-1)
5565 R_j_plus_1 = mk_one(); // by definition of PSC chain
5566 else
5567 R_j_plus_1 = coeff(sRes.get(j+1), x, j+1);
5568 unsigned r = degree(sRes.get(j), x);
5569 if (r == j) {
5570 // sRes_j is regular
5571
5572 exact_pseudo_remainder(sRes.get(j+1), sRes.get(j), x, prem);
5573 TRACE("psc", tout << "j: " << j << "\nsRes_j+1: "; sRes.get(j+1)->display(tout, m_manager);
5574 tout << "\nsRes_j: "; sRes.get(j)->display(tout, m_manager);
5575 tout << "\nprem: " << prem << "\n";);
5576 // sRes_{j-1} = prem/R_j_plus_1^2
5577 newS = exact_div(prem, R_j_plus_1);
5578 newS = exact_div(newS, R_j_plus_1);
5579 sRes.set(j-1, newS);
5580 j--;
5581 }
5582 else {
5583 SASSERT(r < j);
5584 // sRes_j is defective
5585
5586 // sRes_{j-1} = sRes_{j-2} = ... = sRes_{r+1} = 0
5587 for (int i = j-1; i >= static_cast<int>(r+1); i--)
5588 sRes.set(i, mk_zero());
5589
5590 // sRes_r = lc(sRes_j)^{j-r} * sRes_j / R_j_plus_1^{j-r}
5591 newS = lc(sRes.get(j), x);
5592 pw(newS, j-r, newS);
5593 newS = mul(newS, sRes.get(j));
5594 for (unsigned i = 0; i < j-r; i++)
5595 newS = exact_div(newS, R_j_plus_1);
5596 sRes.set(r, newS);
5597
5598 // If r > 0, we also compute sRes_{r-1}
5599 if (r > 0) {
5600 exact_pseudo_remainder(sRes.get(j+1), sRes.get(j), x, prem);
5601 // sRes_{r-1} = prem/(-R_j_plus_1)^{j-r+2}
5602 newS = prem;
5603 for (unsigned i = 0; i < j-r+2; i++)
5604 newS = exact_div(newS, R_j_plus_1);
5605 if ((j-r+2)%2 == 1)
5606 newS = neg(newS);
5607 sRes.set(r-1, newS);
5608 j = r - 1;
5609 }
5610 else {
5611 j = 0;
5612 }
5613 }
5614 }
5615 }
5616
psc_chain1polynomial::manager::imp5617 void psc_chain1(polynomial const * p, polynomial const * q, var x, polynomial_ref_vector & S) {
5618 subresultant_chain(p, q, x, S);
5619 unsigned sz = S.size();
5620 TRACE("psc", tout << "subresultant_chain\n";
5621 for (unsigned i = 0; i < sz; i++) { tout << "i: " << i << " "; S.get(i)->display(tout, m_manager); tout << "\n"; });
5622 for (unsigned i = 0; i < sz - 1; i++) {
5623 S.set(i, coeff(S.get(i), x, i));
5624 }
5625 S.set(sz-1, mk_one());
5626 }
5627
5628 // Store in S a list of the non-zero principal subresultant coefficients of A and B
5629 // If i < j then psc_{i}(A,B) precedes psc_{j}(A,B) in S.
5630 // The leading coefficients of A and B are not included in S.
psc_chain2polynomial::manager::imp5631 void psc_chain2(polynomial const * A, polynomial const * B, var x, polynomial_ref_vector & S) {
5632 polynomial_ref G1(pm());
5633 polynomial_ref G2(pm());
5634 polynomial_ref G3(pm());
5635 polynomial_ref Gh3(pm());
5636 polynomial_ref g1(pm()), h0(pm()), hs0(pm()), h1(pm()), hs1(pm());
5637 unsigned n1 = degree(A, x);
5638 unsigned n2 = degree(B, x);
5639 if (n1 > n2) {
5640 G1 = const_cast<polynomial*>(A);
5641 G2 = const_cast<polynomial*>(B);
5642 }
5643 else {
5644 G1 = const_cast<polynomial*>(B);
5645 G2 = const_cast<polynomial*>(A);
5646 std::swap(n1, n2);
5647 }
5648 unsigned d0 = 0;
5649 unsigned d1 = n1 - n2;
5650 unsigned i = 1;
5651 unsigned n3;
5652 S.reset();
5653 while (true) {
5654 // Compute Gh_{i+2}
5655 if (!is_zero(G2)) {
5656 exact_pseudo_remainder(G1, G2, x, Gh3);
5657 n3 = degree(Gh3, x);
5658 if (!is_zero(Gh3) && d1%2 == 0)
5659 Gh3 = neg(Gh3);
5660 }
5661
5662 // Compute hi
5663 if (i > 1) {
5664 g1 = lc(G1, x);
5665 pw(g1, d0, h1);
5666 if (i > 2) {
5667 pw(h0, d0 - 1, hs0);
5668 h1 = exact_div(h1, hs0);
5669 S.push_back(h1);
5670 if (is_zero(G2)) {
5671 std::reverse(S.data(), S.data() + S.size());
5672 return;
5673 }
5674 }
5675 }
5676
5677 // Compute G_{i+2}
5678 if (i == 1 || is_zero(Gh3)) {
5679 G3 = Gh3;
5680 }
5681 else {
5682 pw(h1, d1, hs1);
5683 hs1 = mul(g1, hs1);
5684 G3 = exact_div(Gh3, hs1);
5685 hs1 = nullptr;
5686 }
5687
5688 // prepare for next iteration
5689 n1 = n2;
5690 n2 = n3;
5691 d0 = d1;
5692 d1 = n1 - n2;
5693 G1 = G2;
5694 G2 = G3;
5695 if (i > 1)
5696 h0 = h1;
5697 i = i + 1;
5698 }
5699 }
5700
5701 // Optimized calculation of S_e using "Dichotomous Lazard"
Se_Lazardpolynomial::manager::imp5702 void Se_Lazard(unsigned d, polynomial const * lc_S_d, polynomial const * S_d_1, var x, polynomial_ref & S_e) {
5703 unsigned n = d - degree(S_d_1, x) - 1;
5704 TRACE("Lazard", tout << "lc_S_d: "; lc_S_d->display(tout, m_manager); tout << "\nS_d_1: "; S_d_1->display(tout, m_manager);
5705 tout << "\nn: " << n << "\n";);
5706 if (n == 0) {
5707 S_e = const_cast<polynomial*>(S_d_1);
5708 return;
5709 }
5710 polynomial_ref X(pm());
5711 X = lc(S_d_1, x);
5712 polynomial const * Y = lc_S_d;
5713 unsigned a = 1 << log2(n);
5714 TRACE("Lazard", tout << "a: " << a << "\n";);
5715 SASSERT(a <= n);
5716 SASSERT(n < 2*a);
5717 polynomial_ref C(pm());
5718 C = X;
5719 n = n - a;
5720 while (a != 1) {
5721 a = a / 2;
5722 // C <- C^2/Y
5723 C = mul(C, C);
5724 C = exact_div(C, Y);
5725 TRACE("Lazard", tout << "loop a: " << a << "\nC : " << C << "\n";);
5726 if (n >= a) {
5727 // C <- C*X/Y
5728 C = mul(C, X);
5729 C = exact_div(C, Y);
5730 n = n - a;
5731 TRACE("Lazard", tout << "if, C: " << C << "\n";);
5732 }
5733 }
5734 TRACE("Lazard", tout << "C: " << C << "\nY: " << Y << "\n";);
5735 S_e = mul(C, S_d_1);
5736 S_e = exact_div(S_e, Y);
5737 }
5738
5739 // Optimized calculation of S_{e-1} for optimized psc_chain
optimized_S_e_1polynomial::manager::imp5740 void optimized_S_e_1(unsigned d, unsigned e, polynomial const * A, polynomial const * S_d_1, polynomial const * S_e, polynomial const * s,
5741 var x, polynomial_ref & S_e_1) {
5742 SASSERT(d == degree(A, x));
5743 SASSERT(e == degree(S_d_1, x));
5744 polynomial_ref c_d_1(pm()), s_e(pm()), x_j(pm()), tmp(pm());
5745 c_d_1 = lc(S_d_1, x);
5746 s_e = lc(S_e, x);
5747 polynomial_ref_buffer H(pm());
5748 x_j = mk_one();
5749 for (unsigned j = 0; j <= e - 1; j++) {
5750 // H_j <- s_e * x^j
5751 x_j = mk_polynomial(x, j);
5752 H.push_back(mul(s_e, x_j));
5753 }
5754 SASSERT(H.size() == e);
5755 // H_e <- s_e * x^e - S_e
5756 x_j = mk_polynomial(x, e);
5757 x_j = mul(s_e, x_j);
5758 H.push_back(sub(x_j, S_e));
5759 SASSERT(H.size() == e+1);
5760 polynomial_ref x_pol(pm()), xH(pm()), xHe(pm());
5761 x_pol = mk_polynomial(x, 1);
5762 for (unsigned j = e + 1; j <= d - 1; j++) {
5763 // H_j <- x H_{j-1} - (coeff(x H_{j-1}, e) * S_{d-1})/c_{d-1}
5764 xH = mul(x_pol, H[j-1]);
5765 xHe = coeff(xH, x, e);
5766 tmp = mul(xHe, S_d_1);
5767 tmp = exact_div(tmp, c_d_1);
5768 H.push_back(sub(xH, tmp));
5769 }
5770 SASSERT(H.size() == d);
5771 // D <- (Sum coeff(A,j) * H[j])/lc(A)
5772 polynomial_ref D(pm());
5773 D = mk_zero();
5774 for (unsigned j = 0; j < d; j++) {
5775 tmp = coeff(A, x, j);
5776 tmp = mul(tmp, H[j]);
5777 D = add(D, tmp);
5778 }
5779 polynomial_ref lc_A(pm());
5780 lc_A = lc(A, x);
5781 D = exact_div(D, lc_A);
5782 // S_e_1 = (-1)^(d-e+1) [c_{d-1} (x H[j-1] + D) - coeff(x H[j-1], e)*S_d-1]/s
5783 xH = mul(x_pol, H[d-1]);
5784 xHe = coeff(xH, x, e);
5785 xHe = mul(xHe, S_d_1);
5786 S_e_1 = add(xH, D);
5787 S_e_1 = mul(c_d_1, S_e_1);
5788 S_e_1 = sub(S_e_1, xHe);
5789 S_e_1 = exact_div(S_e_1, s);
5790 if ((d-e+1) % 2 == 1)
5791 S_e_1 = neg(S_e_1);
5792 }
5793
psc_chain_optimized_corepolynomial::manager::imp5794 void psc_chain_optimized_core(polynomial const * P, polynomial const * Q, var x, polynomial_ref_vector & S) {
5795 TRACE("psc_chain_classic", tout << "P: "; P->display(tout, m_manager); tout << "\nQ: "; Q->display(tout, m_manager); tout << "\n";);
5796 unsigned degP = degree(P, x);
5797 unsigned degQ = degree(Q, x);
5798 SASSERT(degP >= degQ);
5799 polynomial_ref A(pm()), B(pm()), C(pm()), minus_Q(pm()), lc_Q(pm()), ps(pm());
5800
5801 lc_Q = lc(Q, x);
5802 polynomial_ref s(pm());
5803 // s <- lc(Q)^(deg(P)-deg(Q))
5804 pw(lc_Q, degP - degQ, s);
5805 minus_Q = neg(Q);
5806 // A <- Q
5807 A = const_cast<polynomial*>(Q);
5808 // B <- prem(P, -Q)
5809 exact_pseudo_remainder(P, minus_Q, x, B);
5810 while (true) {
5811 unsigned d = degree(A, x);
5812 unsigned e = degree(B, x);
5813 if (is_zero(B))
5814 return;
5815 TRACE("psc_chain_classic", tout << "A: " << A << "\nB: " << B << "\ns: " << s << "\nd: " << d << ", e: " << e << "\n";);
5816 // B is S_{d-1}
5817 ps = coeff(B, x, d-1);
5818 if (!is_zero(ps))
5819 S.push_back(ps);
5820 SASSERT(d >= e);
5821 unsigned delta = d - e;
5822 if (delta > 1) {
5823 // C <- S_e
5824 // Optimized S_e calculation
5825 // s = lc(S_d) at this point
5826 Se_Lazard(d, s, B, x, C);
5827
5828 // C is S_e
5829 ps = coeff(C, x, e);
5830 if (!is_zero(ps))
5831 S.push_back(ps);
5832 }
5833 else {
5834 SASSERT(delta == 0 || delta == 1);
5835 C = B;
5836 }
5837 if (e == 0)
5838 return;
5839 // B <- optimized S_e_1
5840 optimized_S_e_1(d, e, A, B, C, s, x, B);
5841 // A <- C
5842 A = C;
5843 // s <- lc(A)
5844 s = lc(A, x);
5845 }
5846 }
5847
psc_chain_optimizedpolynomial::manager::imp5848 void psc_chain_optimized(polynomial const * P, polynomial const * Q, var x, polynomial_ref_vector & S) {
5849 SASSERT(degree(P, x) > 0);
5850 SASSERT(degree(Q, x) > 0);
5851 S.reset();
5852 if (degree(P, x) >= degree(Q, x))
5853 psc_chain_optimized_core(P, Q, x, S);
5854 else
5855 psc_chain_optimized_core(Q, P, x, S);
5856 if (S.empty())
5857 S.push_back(mk_zero());
5858 std::reverse(S.data(), S.data() + S.size());
5859 }
5860
psc_chain_classic_corepolynomial::manager::imp5861 void psc_chain_classic_core(polynomial const * P, polynomial const * Q, var x, polynomial_ref_vector & S) {
5862 TRACE("psc_chain_classic", tout << "P: "; P->display(tout, m_manager); tout << "\nQ: "; Q->display(tout, m_manager); tout << "\n";);
5863 unsigned degP = degree(P, x);
5864 unsigned degQ = degree(Q, x);
5865 SASSERT(degP >= degQ);
5866 polynomial_ref A(pm()), B(pm()), C(pm()), minus_Q(pm()), lc_Q(pm()), lc_B(pm()), lc_A(pm());
5867 polynomial_ref tmp1(pm()), tmp2(pm()), s_delta(pm()), minus_B(pm()), ps(pm());
5868
5869 lc_Q = lc(Q, x);
5870 polynomial_ref s(pm());
5871 // s <- lc(Q)^(deg(P)-deg(Q))
5872 pw(lc_Q, degP - degQ, s);
5873 minus_Q = neg(Q);
5874 // A <- Q
5875 A = const_cast<polynomial*>(Q);
5876 // B <- prem(P, -Q)
5877 exact_pseudo_remainder(P, minus_Q, x, B);
5878 while (true) {
5879 unsigned d = degree(A, x);
5880 unsigned e = degree(B, x);
5881 if (is_zero(B))
5882 return;
5883 TRACE("psc_chain_classic", tout << "A: " << A << "\nB: " << B << "\ns: " << s << "\nd: " << d << ", e: " << e << "\n";);
5884 // B is S_{d-1}
5885 ps = coeff(B, x, d-1);
5886 if (!is_zero(ps))
5887 S.push_back(ps);
5888 unsigned delta = d - e;
5889 if (delta > 1) {
5890 // C <- S_e
5891 // Standard S_e calculation
5892 // C <- (lc(B)^(delta-1) B) / s^(delta-1)
5893 lc_B = lc(B, x);
5894 pw(lc_B, delta-1, lc_B);
5895 lc_B = mul(lc_B, B);
5896 pw(s, delta - 1, s_delta); // s_delta <- s^(delta-1)
5897 C = exact_div(lc_B, s_delta);
5898
5899 // s_delta <- s^delta
5900 s_delta = mul(s_delta, s);
5901 // C is S_e
5902 ps = coeff(C, x, e);
5903 if (!is_zero(ps))
5904 S.push_back(ps);
5905
5906 }
5907 else {
5908 SASSERT(delta == 0 || delta == 1);
5909 C = B;
5910 // s_delta <- s^delta
5911 pw(s, delta, s_delta);
5912 }
5913 if (e == 0)
5914 return;
5915 // B <- prem(A, -B)/(s^delta * lc(A)
5916 lc_A = lc(A, x);
5917 minus_B = neg(B);
5918 exact_pseudo_remainder(A, minus_B, x, tmp1);
5919 tmp2 = mul(lc_A, s_delta);
5920 B = exact_div(tmp1, tmp2);
5921 // A <- C
5922 A = C;
5923 // s <- lc(A)
5924 s = lc(A, x);
5925 }
5926 }
5927
psc_chain_classicpolynomial::manager::imp5928 void psc_chain_classic(polynomial const * P, polynomial const * Q, var x, polynomial_ref_vector & S) {
5929 SASSERT(degree(P, x) > 0);
5930 SASSERT(degree(Q, x) > 0);
5931 S.reset();
5932 if (degree(P, x) >= degree(Q, x))
5933 psc_chain_classic_core(P, Q, x, S);
5934 else
5935 psc_chain_classic_core(Q, P, x, S);
5936 if (S.empty())
5937 S.push_back(mk_zero());
5938 std::reverse(S.data(), S.data() + S.size());
5939 }
5940
psc_chainpolynomial::manager::imp5941 void psc_chain(polynomial const * A, polynomial const * B, var x, polynomial_ref_vector & S) {
5942 // psc_chain1(A, B, x, S);
5943 //psc_chain2(A, B, x, S);
5944 //psc_chain_classic(A, B, x, S);
5945 psc_chain_optimized(A, B, x, S);
5946 }
5947
normalizepolynomial::manager::imp5948 polynomial * normalize(polynomial const * p) {
5949 if (is_zero(p))
5950 return const_cast<polynomial*>(p);
5951 unsigned sz = p->size();
5952 if (m().modular()) {
5953 unsigned i = 0;
5954 for (; i < sz; i++) {
5955 if (!m().is_p_normalized(p->a(i)))
5956 break;
5957 }
5958 if (i < sz) {
5959 m_cheap_som_buffer.reset();
5960 scoped_numeral a(m_manager);
5961 for (unsigned i = 0; i < sz; i++) {
5962 monomial * m = p->m(i);
5963 m_manager.set(a, p->a(i));
5964 m_cheap_som_buffer.add_reset(a, m);
5965 }
5966 m_cheap_som_buffer.normalize();
5967 return m_cheap_som_buffer.mk();
5968 }
5969 }
5970 scoped_numeral g(m_manager);
5971 m_manager.gcd(sz, p->as(), g);
5972 if (m_manager.is_one(g))
5973 return const_cast<polynomial*>(p);
5974 m_cheap_som_buffer.reset();
5975 scoped_numeral a(m_manager);
5976 for (unsigned i = 0; i < sz; i++) {
5977 monomial * m = p->m(i);
5978 m_manager.div(p->a(i), g, a);
5979 m_cheap_som_buffer.add_reset(a, m);
5980 }
5981 return m_cheap_som_buffer.mk();
5982 }
5983
negpolynomial::manager::imp5984 polynomial * neg(polynomial const * p) {
5985 SASSERT(m_cheap_som_buffer.empty());
5986 scoped_numeral minus_a(m_manager);
5987 unsigned sz = p->size();
5988 for (unsigned i = 0; i < sz; i++) {
5989 m_manager.set(minus_a, p->a(i));
5990 m_manager.neg(minus_a);
5991 m_cheap_som_buffer.add(minus_a, p->m(i));
5992 }
5993 return m_cheap_som_buffer.mk();
5994 }
5995
5996 // Return true if is a(i)*m(i) is a perfect square.
5997 // Store sqrt of a(i) in sqrt_a
is_perfect_squarepolynomial::manager::imp5998 bool is_perfect_square(polynomial const * p, unsigned i, numeral & sqrt_a) {
5999 monomial * m = p->m(i);
6000 if (!m->is_square())
6001 return false;
6002 numeral const & a = p->a(i);
6003 if (!m_manager.is_perfect_square(a, sqrt_a))
6004 return false;
6005 return true;
6006 }
6007
sqrtpolynomial::manager::imp6008 bool sqrt(polynomial const * p, polynomial_ref & r) {
6009 SASSERT(p != 0);
6010 if (is_zero(p)) {
6011 r = const_cast<polynomial*>(p);
6012 return true;
6013 }
6014 scoped_numeral a(m_manager);
6015 TRACE("sqrt_bug",
6016 tout << "sqrt: "; p->display(tout, m_manager); tout << "\n";
6017 tout << "min pos: " << p->graded_lex_min_pos() << "\n";
6018 tout << "max pos: " << p->graded_lex_max_pos() << "\n";);
6019 // Quick Check: the minimal monomial must be a perfect square
6020 unsigned min_pos = p->graded_lex_min_pos();
6021 if (!is_perfect_square(p, min_pos, a))
6022 return false;
6023 // Quick Check: the maximal monomial must be a perfect square
6024 unsigned max_pos = p->graded_lex_max_pos();
6025 if (!is_perfect_square(p, max_pos, a))
6026 return false;
6027 // Compute square root using
6028 // (m_1 + ... + m_k)^2 ==
6029 // (m_1)m_1
6030 // (2m_1 + m_2)m_2
6031 // (2m_1 + 2m_2 + m_3)m_3
6032 // ...
6033 //
6034 // R <- m1
6035 // C <- p - m1*m1
6036 // while (true) {
6037 // if (is_zero(C))
6038 // return true;
6039 // m <- biggest monomial in C
6040 // if (m is not divisible by 2*m1)
6041 // return false;
6042 // m' <- m/2m1
6043 // C <- C - 2*R*m' - m'*m'
6044 // R <- R + m'
6045 // }
6046 //
6047 // The loop above terminates because total degree of the
6048 // maximal monomial in C decreases in each iteration.
6049 monomial * m1 = sqrt(p->m(max_pos));
6050 SASSERT(m1 != 0);
6051 som_buffer & R = m_som_buffer;
6052 som_buffer & C = m_som_buffer2;
6053 struct scoped_reset { som_buffer& b; scoped_reset(som_buffer& b) :b(b) {} ~scoped_reset() { b.reset(); } };
6054 scoped_reset _rs1(R);
6055 scoped_reset _rs2(C);
6056 R.reset();
6057 C.reset();
6058 numeral two;
6059 m_manager.set(two, 2);
6060 scoped_numeral two_a(m_manager);
6061 m_manager.mul(a, two, two_a);
6062 // R <- a * m1
6063 R.add(a, m1);
6064 // C <- p - m1*m1
6065 unsigned sz = p->size();
6066 for (unsigned i = 0; i < sz; i++) {
6067 if (i == max_pos)
6068 continue;
6069 C.add(p->a(i), p->m(i));
6070 }
6071 scoped_numeral a_i(m_manager);
6072 scoped_numeral aux(m_manager);
6073 monomial_ref m_aux(pm());
6074 while (true) {
6075 checkpoint();
6076 TRACE("sqrt_bug", tout << "R: "; R.display(tout); tout << "C: "; C.display(tout););
6077 unsigned curr_max = C.graded_lex_max_pos();
6078 if (curr_max == UINT_MAX) {
6079 // C is empty
6080 r = R.mk();
6081 return true;
6082 }
6083 monomial * m = C.m(curr_max);
6084 monomial_ref m_i(pm());
6085 // m1 does not divide maximal monomial of C.
6086 if (!div(m, m1, m_i))
6087 return false;
6088
6089 // 2*a does not divide maximal coefficient of C
6090 if (!m_manager.divides(two_a, C.a(curr_max)))
6091 return false;
6092
6093 m_manager.div(C.a(curr_max), two_a, a_i);
6094
6095 // C <- C - 2*R*a_i*m_i - a_i*a_i*m_i*m_i
6096 unsigned R_sz = R.size();
6097 for (unsigned j = 0; j < R_sz; j++) {
6098 if (m_manager.is_zero(R.a(j)))
6099 continue;
6100 m_manager.mul(R.a(j), a_i, aux);
6101 m_manager.mul(aux, two, aux);
6102 m_manager.neg(aux);
6103 m_aux = mul(R.m(j), m_i);
6104 C.add(aux, m_aux);
6105 }
6106 m_manager.mul(a_i, a_i, aux);
6107 m_manager.neg(aux);
6108 m_aux = mul(m_i, m_i);
6109 C.add(aux, m_aux);
6110 // R <- R + a_i*m_i
6111 R.add(a_i, m_i);
6112 }
6113 }
6114
renamepolynomial::manager::imp6115 void rename(unsigned sz, var const * xs) {
6116 TRACE("rename", for (unsigned i = 0; i < sz; i++) tout << xs[i] << " "; tout << "\n";
6117 tout << "polynomials before rename\n";
6118 for (unsigned i = 0; i < m_polynomials.size(); i++) {
6119 if (m_polynomials[i] == 0)
6120 continue;
6121 m_polynomials[i]->display(tout, m_manager);
6122 tout << "\n";
6123 });
6124 mm().rename(sz, xs);
6125 // we must traverse the polynomial vector, and update the first monomial,
6126 // since it may not contain anymore the maximal variable with maximal degree.
6127 for (polynomial* p : m_polynomials) {
6128 if (p != nullptr)
6129 p->make_first_maximal();
6130 SASSERT(!p || p->size() <= 1 || !p->lex_sorted());
6131 }
6132 TRACE("rename",
6133 tout << "polynomials after rename\n";
6134 for (unsigned i = 0; i < m_polynomials.size(); i++) {
6135 if (m_polynomials[i] == 0)
6136 continue;
6137 m_polynomials[i]->display(tout, m_manager);
6138 tout << "\n";
6139 });
6140 }
6141
signpolynomial::manager::imp6142 lbool sign(monomial* m, numeral const& c, svector<lbool> const& sign_of_vars) {
6143 unsigned sz = size(m);
6144 lbool sign1 = m_manager.is_pos(c) ? l_true : l_false;
6145 for (unsigned i = 0; i < sz; ++i) {
6146 var v = get_var(m, i);
6147 unsigned d = degree(m, i);
6148 lbool sign2 = sign_of_vars.get(v, l_undef);
6149 if (sign2 == l_undef)
6150 return l_undef;
6151 else if (1 == (d % 2) && sign2 == l_false) {
6152 sign1 = sign1 == l_true ? l_false : l_true;
6153 }
6154 }
6155 return sign1;
6156 }
6157
signpolynomial::manager::imp6158 lbool sign(polynomial const * p, svector<lbool> const& sign_of_vars) {
6159 unsigned sz = size(p);
6160 if (sz == 0) return l_undef;
6161 lbool sign1 = sign(p->m(0), p->a(0), sign_of_vars);
6162 for (unsigned i = 1; sign1 != l_undef && i < sz; ++i) {
6163 if (sign(p->m(i), p->a(i), sign_of_vars) != sign1)
6164 return l_undef;
6165 }
6166 return sign1;
6167 }
6168
is_pospolynomial::manager::imp6169 bool is_pos(polynomial const * p) {
6170 bool found_unit = false;
6171 unsigned sz = p->size();
6172 for (unsigned i = 0; i < sz; i++) {
6173 if (!p->m(i)->is_power_of_two())
6174 return false;
6175 if (p->m(i) == mk_unit())
6176 found_unit = true;
6177 if (!m_manager.is_pos(p->a(i)))
6178 return false;
6179 }
6180 return found_unit;
6181 }
6182
is_negpolynomial::manager::imp6183 bool is_neg(polynomial const * p) {
6184 bool found_unit = false;
6185 unsigned sz = p->size();
6186 for (unsigned i = 0; i < sz; i++) {
6187 if (!p->m(i)->is_power_of_two())
6188 return false;
6189 if (p->m(i) == mk_unit())
6190 found_unit = true;
6191 if (!m_manager.is_neg(p->a(i)))
6192 return false;
6193 }
6194 return found_unit;
6195 }
6196
is_nonpospolynomial::manager::imp6197 bool is_nonpos(polynomial const * p) {
6198 unsigned sz = p->size();
6199 for (unsigned i = 0; i < sz; i++) {
6200 if (!p->m(i)->is_power_of_two())
6201 return false;
6202 if (!m_manager.is_neg(p->a(i)))
6203 return false;
6204 }
6205 return true;
6206 }
6207
is_nonnegpolynomial::manager::imp6208 bool is_nonneg(polynomial const * p) {
6209 unsigned sz = p->size();
6210 for (unsigned i = 0; i < sz; i++) {
6211 if (!p->m(i)->is_power_of_two())
6212 return false;
6213 if (!m_manager.is_pos(p->a(i)))
6214 return false;
6215 }
6216 return true;
6217 }
6218
6219 // Functor used to compute the maximal degree of each variable in a polynomial p.
6220 class var_max_degree {
6221 unsigned_vector m_max_degree;
6222 var_vector m_xs;
6223 public:
init(polynomial const * p)6224 void init(polynomial const * p) {
6225 unsigned sz = p->size();
6226 for (unsigned i = 0; i < sz; i++) {
6227 monomial * m = p->m(i);
6228 unsigned msz = m->size();
6229 for (unsigned j = 0; j < msz; j++) {
6230 var x = m->get_var(j);
6231 unsigned k = m->degree(j);
6232 unsigned max_k = m_max_degree.get(x, 0);
6233 if (k > max_k) {
6234 if (max_k == 0)
6235 m_xs.push_back(x);
6236 m_max_degree.setx(x, m->degree(j), 0);
6237 }
6238 }
6239 }
6240 }
6241
reset()6242 void reset() {
6243 auto sz = m_xs.size();
6244 for (unsigned i = 0; i < sz; i++) {
6245 m_max_degree[m_xs[i]] = 0;
6246 }
6247 m_xs.reset();
6248 }
6249
operator ()(var x) const6250 unsigned operator()(var x) const {
6251 return m_max_degree.get(x, 0);
6252 }
6253
num_vars() const6254 unsigned num_vars() const { return m_xs.size(); }
6255
vars() const6256 var const * vars() const { return m_xs.data(); }
6257 };
6258
6259 struct scoped_var_max_degree {
6260 var_max_degree & m;
scoped_var_max_degreepolynomial::manager::imp::scoped_var_max_degree6261 scoped_var_max_degree(var_max_degree & _m, polynomial const * p):
6262 m(_m) {
6263 m.init(p);
6264 }
~scoped_var_max_degreepolynomial::manager::imp::scoped_var_max_degree6265 ~scoped_var_max_degree() {
6266 m.reset();
6267 }
operator ()polynomial::manager::imp::scoped_var_max_degree6268 unsigned operator()(var x) const {
6269 return m(x);
6270 }
num_varspolynomial::manager::imp::scoped_var_max_degree6271 unsigned num_vars() const { return m.num_vars(); }
varspolynomial::manager::imp::scoped_var_max_degree6272 var const * vars() const { return m.vars(); }
6273 };
6274
6275 var_max_degree m_var_max_degree;
6276
6277 // This method uses the tmp fields: m_found_vars, m_var_max_degree.
substitutepolynomial::manager::imp6278 polynomial * substitute(polynomial const * p, var2mpq const & x2v) {
6279 scoped_var_max_degree var2max_degree(m_var_max_degree, p);
6280 unsigned xs_sz = var2max_degree.num_vars();
6281 var const * xs = var2max_degree.vars();
6282 bool found = false;
6283 for (unsigned i = 0; i < xs_sz; i++) {
6284 var x = xs[i];
6285 if (x2v.contains(x) && var2max_degree(x) > 0) {
6286 found = true;
6287 break;
6288 }
6289 }
6290 if (!found)
6291 return const_cast<polynomial*>(p);
6292 scoped_numeral new_a(m_manager);
6293 scoped_numeral tmp(m_manager);
6294 m_found_vars.reserve(num_vars(), false);
6295 m_som_buffer.reset();
6296 som_buffer & R = m_som_buffer;
6297 tmp_monomial & new_m = m_tmp1;
6298 unsigned sz = p->size();
6299 for (unsigned i = 0; i < sz; i++) {
6300 monomial * m = p->m(i);
6301 unsigned msz = m->size();
6302 unsigned new_msz = 0;
6303 m_manager.set(new_a, p->a(i));
6304 new_m.reserve(msz);
6305 for (unsigned j = 0; j < msz; j++) {
6306 var x = m->get_var(j);
6307 unsigned k = m->degree(j);
6308 if (x2v.contains(x)) {
6309 unsigned max_k = var2max_degree(x);
6310 m_found_vars[x] = true;
6311 mpq const & x_value = x2v(x);
6312 m_manager.power(x_value.numerator(), k, tmp);
6313 m_manager.mul(tmp, new_a, new_a);
6314 if (k < max_k) {
6315 m_manager.power(x_value.denominator(), max_k - k, tmp);
6316 m_manager.mul(tmp, new_a, new_a);
6317 }
6318 }
6319 else {
6320 new_m.set_power(new_msz, m->get_power(j));
6321 new_msz++;
6322 }
6323 }
6324 // For each variable x in xs that does not occur in m, I
6325 // should include (x2v(x).denominator())^{var2max_degree(x)} to new_a
6326 for (unsigned j = 0; j < xs_sz; j++) {
6327 var x = xs[j];
6328 if (m_found_vars[x])
6329 continue;
6330 if (x2v.contains(x)) {
6331 m_manager.power(x2v(x).denominator(), var2max_degree(x), tmp);
6332 m_manager.mul(tmp, new_a, new_a);
6333 }
6334 }
6335 // Reset m_found_vars
6336 for (unsigned j = 0; j < msz; j++) {
6337 var x = m->get_var(j);
6338 m_found_vars[x] = false;
6339 }
6340 // Add new_a*new_m to R
6341 if (!m_manager.is_zero(new_a)) {
6342 new_m.set_size(new_msz);
6343 R.add(new_a, mk_monomial(new_m));
6344 }
6345 }
6346 return R.mk(true);
6347 }
6348
6349 struct var_pos {
6350 unsigned_vector m_pos;
6351
initpolynomial::manager::imp::var_pos6352 void init(unsigned sz, var const * xs) {
6353 for (unsigned i = 0; i < sz; i++) {
6354 SASSERT(m_pos.get(xs[i], UINT_MAX) == UINT_MAX);
6355 m_pos.setx(xs[i], i, UINT_MAX);
6356 }
6357 }
6358
resetpolynomial::manager::imp::var_pos6359 void reset(unsigned sz, var const * xs) {
6360 for (unsigned i = 0; i < sz; i++) {
6361 SASSERT(m_pos.get(xs[i], UINT_MAX) != UINT_MAX);
6362 m_pos[xs[i]] = UINT_MAX;
6363 }
6364 }
6365
operator ()polynomial::manager::imp::var_pos6366 unsigned operator()(var x) const { return m_pos.get(x, UINT_MAX); }
6367 };
6368
6369 struct scoped_var_pos {
6370 var_pos & m;
6371 unsigned m_xs_sz;
6372 var const * m_xs;
scoped_var_pospolynomial::manager::imp::scoped_var_pos6373 scoped_var_pos(var_pos & p, unsigned xs_sz, var const * xs):
6374 m(p),
6375 m_xs_sz(xs_sz),
6376 m_xs(xs) {
6377 m.init(m_xs_sz, m_xs);
6378 }
~scoped_var_pospolynomial::manager::imp::scoped_var_pos6379 ~scoped_var_pos() {
6380 m.reset(m_xs_sz, m_xs);
6381 }
operator ()polynomial::manager::imp::scoped_var_pos6382 unsigned operator()(var x) const { return m(x); }
6383 };
6384
6385 var_pos m_var_pos;
6386
6387 struct var2mpq_wrapper : public var2mpq {
6388 scoped_var_pos m_var_pos;
6389 mpq const * m_vs;
var2mpq_wrapperpolynomial::manager::imp::var2mpq_wrapper6390 var2mpq_wrapper(unsigned xs_sz, var const * xs, mpq const * vs, var_pos & buffer):
6391 m_var_pos(buffer, xs_sz, xs),
6392 m_vs(vs) {
6393 }
mpolynomial::manager::imp::var2mpq_wrapper6394 unsynch_mpq_manager & m() const override { UNREACHABLE(); static unsynch_mpq_manager m; return m; }
containspolynomial::manager::imp::var2mpq_wrapper6395 bool contains(var x) const override { return m_var_pos(x) != UINT_MAX; }
operator ()polynomial::manager::imp::var2mpq_wrapper6396 mpq const & operator()(var x) const override { return m_vs[m_var_pos(x)]; }
6397 };
6398
substitutepolynomial::manager::imp6399 polynomial * substitute(polynomial const * p, unsigned xs_sz, var const * xs, mpq const * vs) {
6400 var2mpq_wrapper x2v(xs_sz, xs, vs, m_var_pos);
6401 return substitute(p, x2v);
6402 }
6403
substitutepolynomial::manager::imp6404 polynomial * substitute(polynomial const * p, unsigned xs_sz, var const * xs, numeral const * vs) {
6405 TRACE("polynomial", tout << "substitute num_vars: " << xs_sz << "\n";
6406 for (unsigned i = 0; i < xs_sz; i++) { tout << "x" << xs[i] << " -> " << m_manager.to_string(vs[i]) << "\n"; });
6407 scoped_var_pos var2pos(m_var_pos, xs_sz, xs);
6408 scoped_numeral new_a(m_manager);
6409 scoped_numeral tmp(m_manager);
6410 m_som_buffer.reset();
6411 som_buffer & R = m_som_buffer;
6412 tmp_monomial & new_m = m_tmp1;
6413 unsigned sz = p->size();
6414 for (unsigned i = 0; i < sz; i++) {
6415 monomial * m = p->m(i);
6416 unsigned msz = m->size();
6417 unsigned new_msz = 0;
6418 m_manager.set(new_a, p->a(i));
6419 new_m.reserve(msz);
6420 for (unsigned j = 0; j < msz; j++) {
6421 var x = m->get_var(j);
6422 unsigned k = m->degree(j);
6423 unsigned pos = var2pos(x);
6424 if (pos != UINT_MAX) {
6425 m_manager.power(vs[pos], k, tmp);
6426 m_manager.mul(tmp, new_a, new_a);
6427 }
6428 else {
6429 SASSERT(var2pos(x) == UINT_MAX); // x is not in xs
6430 new_m.set_power(new_msz, m->get_power(j));
6431 new_msz++;
6432 }
6433 }
6434 new_m.set_size(new_msz);
6435 TRACE("polynomial", tout << "processing " << m_manager.to_string(p->a(i)) << " "; m->display(tout); tout << "\n";
6436 tout << "new_a: " << m_manager.to_string(new_a) << " "; mk_monomial(new_m)->display(tout); tout << "\n";);
6437 R.add(new_a, mk_monomial(new_m));
6438 }
6439 return R.mk();
6440 }
6441
substitutepolynomial::manager::imp6442 void substitute(polynomial const* r, var x, polynomial const* p, polynomial const* q, polynomial_ref& result) {
6443 unsigned md = degree(r, x);
6444 if (md == 0) {
6445 result = const_cast<polynomial*>(r);
6446 return;
6447 }
6448 result = nullptr;
6449 polynomial_ref p1(pm()), q1(pm());
6450 polynomial_ref_buffer ps(pm());
6451 unsigned sz = r->size();
6452 for (unsigned i = 0; i < sz; i++) {
6453 monomial * m0 = r->m(i);
6454 unsigned dm = m0->degree_of(x);
6455 SASSERT(md >= dm);
6456 monomial_ref m1(div_x(m0, x), pm());
6457 pw(p, dm, p1);
6458 pw(q, md - dm, q1);
6459 p1 = mul(r->a(i), m1, p1 * q1);
6460 if (result)
6461 result = add(result, p1);
6462 else
6463 result = p1;
6464 }
6465 }
6466
6467
6468 /**
6469 Auxiliary method used to implement t_eval.
6470 If evaluates the sub-polynomial p composed of the monomials at positions [start, end)
6471 where all variables > x are ignored.
6472
6473 var2pos is a mapping from variables to the positions.
6474 vs[var2pos(x)] contains the value of x.
6475 */
6476 template<typename ValManager>
t_eval_corepolynomial::manager::imp6477 void t_eval_core(polynomial * p, ValManager & vm, var2value<ValManager> const & x2v,
6478 unsigned start, unsigned end, var x, typename ValManager::numeral & r) {
6479 TRACE("eval_bug", tout << "p: "; p->display(tout, m()); tout << "\n";
6480 tout << "start: " << start << ", end: " << end << ", x: " << x << "\n";);
6481 SASSERT(start < end);
6482 SASSERT(end <= p->size());
6483 SASSERT(is_valid(x));
6484 _scoped_numeral<ValManager> aux(vm);
6485 if (end == start + 1) {
6486 vm.set(r, p->a(start));
6487 monomial * m = p->m(start);
6488 SASSERT(m->degree_of(x) > 0);
6489 unsigned sz = m->size();
6490 for (unsigned i = 0; i < sz; i++) {
6491 var y = m->get_var(i);
6492 if (y > x)
6493 break;
6494 SASSERT(x2v.contains(y));
6495 unsigned d = m->degree(i);
6496 vm.power(x2v(y), d, aux);
6497 vm.mul(r, aux, r);
6498 }
6499 }
6500 else {
6501 SASSERT(x2v.contains(x));
6502 typename ValManager::numeral const & x_value = x2v(x);
6503 vm.reset(r);
6504 unsigned i = start;
6505 while (i < end) {
6506 checkpoint();
6507 unsigned d = p->m(i)->degree_of(x);
6508 if (d == 0) {
6509 var y = p->max_smaller_than(i, end, x);
6510 if (y == null_var) {
6511 SASSERT(end == i+1);
6512 vm.add(r, p->a(i), r);
6513 }
6514 else {
6515 t_eval_core<ValManager>(p, vm, x2v, i, end, y, aux.get());
6516 vm.add(r, aux, r);
6517 }
6518 break;
6519 }
6520 unsigned j = i+1;
6521 unsigned next_d = 0;
6522 for (; j < end; j++) {
6523 unsigned d_j = p->m(j)->degree_of(x);
6524 SASSERT(d_j <= d);
6525 if (d_j < d) {
6526 next_d = d_j;
6527 break;
6528 }
6529 }
6530 SASSERT(j == end || p->m(j)->degree_of(x) < d);
6531 var y = p->max_smaller_than(i, j, x);
6532 if (y == null_var) {
6533 SASSERT(j == i+1);
6534 vm.set(aux, p->a(i));
6535 }
6536 else {
6537 t_eval_core<ValManager>(p, vm, x2v, i, j, y, aux.get());
6538 }
6539 vm.add(r, aux, r);
6540 vm.power(x_value, d - next_d, aux);
6541 vm.mul(r, aux, r);
6542 i = j;
6543 }
6544 }
6545 TRACE("eval_bug", tout << "result for start: " << start << ", end: " << end << ", x: " << x << "\n";
6546 tout << "r: "; vm.display(tout, r); tout << "\n";);
6547 }
6548
6549 template<typename ValManager>
t_evalpolynomial::manager::imp6550 void t_eval(polynomial * p, var2value<ValManager> const & x2v, typename ValManager::numeral & r) {
6551 ValManager & vm = x2v.m();
6552 if (is_zero(p)) {
6553 vm.reset(r);
6554 return;
6555 }
6556 if (is_const(p)) {
6557 SASSERT(size(p)==1);
6558 vm.set(r, p->a(0));
6559 return;
6560 }
6561 lex_sort(p); // lex_sort just reorders the monomials of p. That is, p still represents the same polynomial
6562 t_eval_core(p, vm, x2v, 0, p->size(), max_var(p), r);
6563 }
6564
6565 class single_var2value : public var2value<numeral_manager> {
6566 numeral_manager & m_manager;
6567 var m_x;
6568 numeral const & m_val;
6569 public:
single_var2value(numeral_manager & m,var x,numeral const & val)6570 single_var2value(numeral_manager & m, var x, numeral const & val):m_manager(m), m_x(x), m_val(val) {}
m() const6571 numeral_manager & m() const override { return m_manager; }
contains(var x) const6572 bool contains(var x) const override { return m_x == x; }
operator ()(var x) const6573 numeral const & operator()(var x) const override { SASSERT(m_x == x); return m_val; }
6574 };
6575
univ_evalpolynomial::manager::imp6576 void univ_eval(polynomial const * p, var x, numeral const & val, numeral & r) {
6577 SASSERT(is_univariate(p));
6578 if (is_zero(p))
6579 m().set(r, 0);
6580 else if (is_const(p))
6581 m().set(r, p->a(0));
6582 else
6583 t_eval<numeral_manager>(const_cast<polynomial*>(p), single_var2value(m(),x, val), r);
6584 }
6585
evalpolynomial::manager::imp6586 void eval(polynomial const * p, var2mpbqi const & x2v, mpbqi & r) {
6587 t_eval<mpbqi_manager>(const_cast<polynomial*>(p), x2v, r);
6588 }
6589
evalpolynomial::manager::imp6590 void eval(polynomial const * p, var2mpq const & x2v, mpq & r) {
6591 t_eval<unsynch_mpq_manager>(const_cast<polynomial*>(p), x2v, r);
6592 }
6593
evalpolynomial::manager::imp6594 void eval(polynomial const * p, var2anum const & x2v, anum & r) {
6595 t_eval<anum_manager>(const_cast<polynomial*>(p), x2v, r);
6596 }
6597
6598 // Return the variable with minimal degree in p
6599 // That is min_x s.t. forall x in p degree(p, min_x) <= degree(p, x)
get_min_degree_varpolynomial::manager::imp6600 var get_min_degree_var(polynomial const * p) {
6601 SASSERT(!is_const(p));
6602 scoped_var_max_degree var2max_degree(m_var_max_degree, p);
6603 unsigned num_vars = var2max_degree.num_vars();
6604 var const * xs = var2max_degree.vars();
6605 var min_x = null_var;
6606 unsigned deg_min = UINT_MAX;
6607 for (unsigned i = 0; i < num_vars; i++) {
6608 var x_i = xs[i];
6609 unsigned deg_x_i = var2max_degree(x_i);
6610 if (deg_x_i < deg_min) {
6611 min_x = x_i;
6612 deg_min = deg_x_i;
6613 }
6614 }
6615 return min_x;
6616 }
6617
acc_constantpolynomial::manager::imp6618 void acc_constant(factors & r, numeral const & c) {
6619 TRACE("factor_bug", tout << "acc_constant, c: "; m_manager.display(tout, c); tout << "\n";);
6620 scoped_numeral new_c(m_manager);
6621 m_manager.mul(r.get_constant(), c, new_c);
6622 r.set_constant(new_c);
6623 }
6624
flip_signpolynomial::manager::imp6625 void flip_sign(factors & r) {
6626 scoped_numeral new_c(m_manager);
6627 m_manager.set(new_c, r.get_constant());
6628 m_manager.neg(new_c);
6629 r.set_constant(new_c);
6630 }
6631
factor_1_sqf_pppolynomial::manager::imp6632 void factor_1_sqf_pp(polynomial const * p, factors & r, var x, unsigned k) {
6633 SASSERT(degree(p, x) == 1);
6634 SASSERT(is_primitive(p, x));
6635 SASSERT(is_square_free(p, x));
6636 TRACE("factor", tout << "factor square free (degree == 1):\n"; p->display(tout, m_manager); tout << "\n";);
6637 // easy case
6638 r.push_back(const_cast<polynomial*>(p), k);
6639 }
6640
factor_2_sqf_pppolynomial::manager::imp6641 void factor_2_sqf_pp(polynomial const * p, factors & r, var x, unsigned k) {
6642 SASSERT(degree(p, x) == 2);
6643 SASSERT(is_primitive(p, x));
6644 SASSERT(is_square_free(p, x));
6645 TRACE("factor", tout << "factor square free (degree == 2):\n"; p->display(tout, m_manager); tout << "\n";);
6646
6647 polynomial_ref a(pm());
6648 polynomial_ref b(pm());
6649 polynomial_ref c(pm());
6650 a = coeff(p, x, 2);
6651 b = coeff(p, x, 1);
6652 c = coeff(p, x, 0);
6653 TRACE("factor", tout << "a: " << a << "\nb: " << b << "\nc: " << c << "\n";);
6654 // make sure the leading monomoal of a is positive
6655 bool flipped_coeffs = false;
6656 SASSERT(!is_zero(a));
6657 unsigned a_glex_max_pos = a->graded_lex_max_pos();
6658 SASSERT(a_glex_max_pos != UINT_MAX);
6659 if (m_manager.is_neg(a->a(a_glex_max_pos))) {
6660 a = neg(a);
6661 b = neg(b);
6662 c = neg(c);
6663 flipped_coeffs = true;
6664 }
6665 // Create the discriminant: b^2 - 4*a*c
6666 polynomial_ref b2(pm());
6667 b2 = mul(b, b);
6668 polynomial_ref ac(pm());
6669 ac = mul(a, c);
6670 polynomial_ref disc(pm());
6671 numeral m_four;
6672 m_manager.set(m_four, -4);
6673 disc = addmul(b2, m_four, mk_unit(), ac);
6674 // discriminant must be different from 0, since p is square free
6675 SASSERT(!is_zero(disc));
6676 polynomial_ref disc_sqrt(pm());
6677 TRACE("factor", tout << "disc: " << disc << "\n";);
6678 if (!sqrt(disc, disc_sqrt)) {
6679 // p is irreducible
6680 r.push_back(const_cast<polynomial*>(p), k);
6681 return;
6682 }
6683 if (flipped_coeffs && k % 2 == 1) {
6684 // if k is ODD, and we flipped the coefficients,
6685 // we must also flip the sign of r.
6686 flip_sign(r);
6687 }
6688 DEBUG_CODE({
6689 polynomial_ref tmp(pm());
6690 tmp = mul(disc_sqrt, disc_sqrt);
6691 SASSERT(eq(disc, tmp));
6692 });
6693 TRACE("factor", tout << "disc_sqrt: " << disc_sqrt << "\n";);
6694 // p = cont*(2*a*x + b - disc_sqrt)*(2*a*x + b + disc_sqrt)
6695 numeral two;
6696 m_manager.set(two, 2);
6697 polynomial_ref f1(pm());
6698 polynomial_ref f2(pm());
6699 monomial_ref mx(pm());
6700 mx = mk_monomial(x);
6701 polynomial_ref two_ax(pm());
6702 two_ax = mul(two, mx, a);
6703 f1 = add(two_ax, b);
6704 f2 = f1;
6705 f1 = sub(f1, disc_sqrt);
6706 f2 = add(f2, disc_sqrt);
6707 TRACE("factor", tout << "before pp\nf1: " << f1 << "\nf2: " << f2 << "\n";
6708 polynomial_ref cf1(pm()); m_wrapper.content(f1, x, cf1);
6709 polynomial_ref cf2(pm()); m_wrapper.content(f2, x, cf2);
6710 tout << "content(f1): " << cf1 << "\ncontent(f2): " << cf2 << "\n";);
6711 f1 = pp(f1, x);
6712 f2 = pp(f2, x);
6713 TRACE("factor", tout << "f1: " << f1 << "\nf2: " << f2 << "\n";);
6714 DEBUG_CODE({
6715 polynomial_ref f1f2(pm());
6716 f1f2 = mul(f1, f2);
6717 if (flipped_coeffs)
6718 f1f2 = neg(f1f2);
6719 SASSERT(eq(f1f2, p));
6720 });
6721 r.push_back(f1, k);
6722 r.push_back(f2, k);
6723 }
6724
factor_sqf_pp_univpolynomial::manager::imp6725 void factor_sqf_pp_univ(polynomial const * p, factors & r, unsigned k, factor_params const & params) {
6726 SASSERT(is_univariate(p));
6727 SASSERT(is_square_free(p, max_var(p)));
6728 SASSERT(is_primitive(p, max_var(p)));
6729 SASSERT(!is_zero(p));
6730 TRACE("factor", tout << "factor square free univariate:\n"; p->display(tout, m_manager); tout << "\n";);
6731
6732 // Convert polynomial into a upolynomial, and execute univariate factorization.
6733 var x = max_var(p);
6734 up_manager::scoped_numeral_vector p1(upm().m());
6735 polynomial_ref p_ref(pm());
6736 p_ref = const_cast<polynomial*>(p);
6737 upm().to_numeral_vector(p_ref, p1);
6738 up_manager::factors fs(upm());
6739 upolynomial::factor_square_free(upm(), p1, fs, params);
6740 SASSERT(m().is_one(fs.get_constant()) || m().is_minus_one(fs.get_constant()));
6741
6742 if (fs.distinct_factors() == 1 && fs.get_degree(0) == 1) {
6743 // p is irreducible
6744 r.push_back(const_cast<polynomial*>(p), k);
6745 }
6746 else {
6747 // Convert factors back into polynomial objects
6748 TRACE("factor_bug", tout << "factoring fs constant: " << m().to_string(fs.get_constant()) << "\np:\n";
6749 p->display(tout, m()); tout << "\n";);
6750 polynomial_ref f(pm());
6751 unsigned num_factors = fs.distinct_factors();
6752 for (unsigned i = 0; i < num_factors; i++) {
6753 numeral_vector const & f1 = fs[i];
6754 unsigned k1 = fs.get_degree(i);
6755 f = to_polynomial(f1.size(), f1.data(), x);
6756 TRACE("factor_bug",
6757 tout << "uni-factor:\n"; upm().display(tout, f1); tout << "\n";
6758 tout << "factor:\n" << f << "\n";);
6759 r.push_back(f, k*k1);
6760 }
6761 TRACE("factor_bug", tout << "end-factors...\n";);
6762 SASSERT(m().is_one(fs.get_constant()) || m().is_minus_one(fs.get_constant()));
6763 if (m().is_minus_one(fs.get_constant()) && k % 2 == 1)
6764 flip_sign(r);
6765 }
6766 }
6767
factor_n_sqf_pppolynomial::manager::imp6768 void factor_n_sqf_pp(polynomial const * p, factors & r, var x, unsigned k) {
6769 SASSERT(degree(p, x) > 2);
6770 SASSERT(is_primitive(p, x));
6771 SASSERT(is_square_free(p, x));
6772 TRACE("factor", tout << "factor square free (degree > 2):\n"; p->display(tout, m_manager); tout << "\n";);
6773
6774 // TODO: invoke Dejan's procedure
6775 r.push_back(const_cast<polynomial*>(p), k);
6776 }
6777
factor_sqf_pppolynomial::manager::imp6778 void factor_sqf_pp(polynomial const * p, factors & r, var x, unsigned k, factor_params const & params) {
6779 SASSERT(degree(p, x) > 0);
6780 SASSERT(is_primitive(p, x));
6781 SASSERT(is_square_free(p, x));
6782 SASSERT(!is_zero(p));
6783
6784 unsigned deg_x = degree(p, x);
6785 if (deg_x == 1)
6786 factor_1_sqf_pp(p, r, x, k);
6787 else if (is_univariate(p))
6788 factor_sqf_pp_univ(p, r, k, params);
6789 else if (deg_x == 2)
6790 factor_2_sqf_pp(p, r, x, k);
6791 else
6792 factor_n_sqf_pp(p, r, x, k);
6793 }
6794
factor_corepolynomial::manager::imp6795 void factor_core(polynomial const * p, factors & r, factor_params const & params) {
6796 TRACE("factor", tout << "factor_core\np: "; p->display(tout, m_manager); tout << "\n";);
6797 TRACE("factor_bug", tout << "factors r.get_constant(): " << m_manager.to_string(r.get_constant()) << "\n";);
6798 SASSERT(!is_zero(p));
6799 if (is_const(p)) {
6800 SASSERT(!is_zero(p));
6801 SASSERT(p->size() == 1);
6802 acc_constant(r, p->a(0));
6803 return;
6804 }
6805 var x = get_min_degree_var(p);
6806 SASSERT(degree(p, x) > 0);
6807 scoped_numeral i(m_manager);
6808 polynomial_ref c(pm()), pp(pm());
6809 iccp(p, x, i, c, pp);
6810 TRACE("factor", tout << "i: " << i << "\n";);
6811 acc_constant(r, i);
6812 factor_core(c, r, params);
6813
6814 polynomial_ref C(pm());
6815 C = pp;
6816 // Let C be a primitive polynomial of the form: P_1^1 * P_2^2 * ... * P_k^k, where each P_i is square free
6817 polynomial_ref C_prime(pm());
6818 C_prime = derivative(C, x);
6819 polynomial_ref B(pm()), A(pm()), D(pm());
6820 gcd(C, C_prime, B);
6821 if (is_const(B)) {
6822 // C must be of the form P_1 (square free)
6823 SASSERT(degree(C, x) > 0);
6824 factor_sqf_pp(C, r, x, 1, params);
6825 }
6826 else {
6827 // B is of the form P_2 * P_3^2 * ... * P_k^{k-1}
6828 A = exact_div(C, B);
6829 // A is of the form P_1 * P_2 * ... * P_k
6830 unsigned j = 1;
6831 while (!is_const(A)) {
6832 SASSERT(is_primitive(A, x));
6833 SASSERT(is_square_free(A, x));
6834 SASSERT(degree(A, x) > 0);
6835 checkpoint();
6836 TRACE("factor", tout << "factor_core main loop j: " << j << "\nA: " << A << "\nB: " << B << "\n";);
6837 // A is of the form P_j * P_{j+1} * P_{j+2} * ... * P_k
6838 // B is of the form P_{j+1} * P_{j+2}^2 * ... * P_k^{k - j - 2}
6839 gcd(A, B, D);
6840 // D is of the form P_{j+1} * P_{j+2} * ... * P_k
6841 C = exact_div(A, D);
6842 // C is of the form P_j
6843 if (!is_const(C)) {
6844 SASSERT(degree(C, x) > 0);
6845 factor_sqf_pp(C, r, x, j, params);
6846 }
6847 else {
6848 TRACE("factor", tout << "const C: " << C << "\n";);
6849 SASSERT(C->size() == 1);
6850 SASSERT(m_manager.is_one(C->a(0)) || m_manager.is_minus_one(C->a(0)));
6851 if (m_manager.is_minus_one(C->a(0)) && j % 2 == 1)
6852 flip_sign(r);
6853 }
6854 B = exact_div(B, D);
6855 // B is of the form P_{j+2} * ... * P_k^{k - j - 3}
6856 A = D;
6857 // D is of the form P_{j+1} * P_{j+2} * ... * P_k
6858 j++;
6859 }
6860 SASSERT(eq(mk_one(), A));
6861 }
6862 }
6863
factorpolynomial::manager::imp6864 void factor(polynomial const * p, factors & r, factor_params const & params) {
6865 if (is_zero(p)) {
6866 r.set_constant(mpz(0));
6867 return;
6868 }
6869 factor_core(p, r, params);
6870 TRACE("factor_bug", tout << "[factor] end, r.get_constant(): " << m_manager.to_string(r.get_constant()) << "\n";);
6871 }
6872
to_polynomialpolynomial::manager::imp6873 polynomial * to_polynomial(unsigned sz, numeral const * p, var x) {
6874 if (sz == 0)
6875 return mk_zero();
6876 _scoped_numeral_buffer<numeral_manager, 128> coeffs(m_manager);
6877 for (unsigned i = 0; i < sz; i++) {
6878 coeffs.push_back(numeral());
6879 m_manager.set(coeffs.back(), p[i]);
6880 }
6881 return mk_univariate(x, sz-1, coeffs.data());
6882 }
6883
mk_glex_monicpolynomial::manager::imp6884 polynomial * mk_glex_monic(polynomial const * p) {
6885 SASSERT(m_manager.field());
6886 if (is_zero(p))
6887 return const_cast<polynomial*>(p);
6888 unsigned pos = p->graded_lex_max_pos();
6889 if (m_manager.is_one(p->a(pos)))
6890 return const_cast<polynomial*>(p);
6891 scoped_numeral inv_c(m());
6892 scoped_numeral new_a(m());
6893 m().set(inv_c, p->a(pos));
6894 m().inv(inv_c);
6895 m_cheap_som_buffer.reset();
6896 cheap_som_buffer & R = m_cheap_som_buffer;
6897 unsigned sz = p->size();
6898 for (unsigned i = 0; i < sz; i++) {
6899 m().set(new_a, p->a(i));
6900 m().mul(new_a, inv_c, new_a);
6901 R.add(new_a, p->m(i));
6902 }
6903 return R.mk();
6904 }
6905
6906 som_buffer_vector m_translate_buffers;
translatepolynomial::manager::imp6907 polynomial * translate(polynomial const * p, var x, numeral const & v) {
6908 unsigned deg_x = degree(p, x);
6909 if (deg_x == 0 || m().is_zero(v))
6910 return const_cast<polynomial*>(p);
6911 som_buffer_vector & as = m_translate_buffers;
6912 m_translate_buffers.reset(deg_x+1);
6913 coeffs(p, x, as);
6914 for (unsigned i = 1; i <= deg_x; i++) {
6915 checkpoint();
6916 for (unsigned k = deg_x-i; k <= deg_x-1; k++) {
6917 as[k]->addmul(v, as[k+1]);
6918 }
6919 }
6920 monomial_ref xk(pm());
6921 som_buffer & R = m_som_buffer;
6922 R.reset();
6923 for (unsigned k = 0; k <= deg_x; k++) {
6924 xk = mk_monomial(x, k);
6925 R.addmul(xk, as[k]);
6926 }
6927 m_translate_buffers.reset(deg_x+1);
6928 return R.mk();
6929 }
6930
translatepolynomial::manager::imp6931 void translate(polynomial const * p, unsigned xs_sz, var const * xs, numeral const * vs, polynomial_ref & r) {
6932 r = const_cast<polynomial*>(p);
6933 if (xs_sz == 0 || is_const(p))
6934 return;
6935 for (unsigned i = 0; i < xs_sz; i++)
6936 r = translate(r, xs[i], vs[i]);
6937 }
6938
mod_dpolynomial::manager::imp6939 polynomial * mod_d(polynomial const * p, var2degree const & x2d) {
6940 if (is_const(p))
6941 return const_cast<polynomial*>(p);
6942
6943 cheap_som_buffer & R = m_cheap_som_buffer;
6944 R.reset();
6945 unsigned sz = p->size();
6946 for (unsigned i = 0; i < sz; i++) {
6947 monomial * m = p->m(i);
6948 unsigned msz = m->size();
6949 unsigned j;
6950 for (j = 0; j < msz; j++) {
6951 var x = m->get_var(j);
6952 unsigned dx = x2d.degree(x);
6953 if (dx == 0)
6954 continue;
6955 if (m->degree(j) >= dx)
6956 break;
6957 }
6958 if (j == msz) {
6959 R.add(p->a(i), p->m(i));
6960 }
6961 }
6962 return R.mk();
6963 }
6964
exact_pseudo_division_mod_dpolynomial::manager::imp6965 void exact_pseudo_division_mod_d(polynomial const * p, polynomial const * q, var x, var2degree const & x2d, polynomial_ref & Q, polynomial_ref & R) {
6966 unsigned d;
6967 pseudo_division_core<true, true, true>(p, q, x, d, Q, R, &x2d);
6968 }
6969 };
6970
manager(reslimit & lim,numeral_manager & m,monomial_manager * mm)6971 manager::manager(reslimit& lim, numeral_manager & m, monomial_manager * mm) {
6972 m_imp = alloc(imp, lim, *this, m, mm);
6973 }
6974
manager(reslimit & lim,numeral_manager & m,small_object_allocator * a)6975 manager::manager(reslimit& lim, numeral_manager & m, small_object_allocator * a) {
6976 m_imp = alloc(imp, lim, *this, m, a);
6977 }
6978
~manager()6979 manager::~manager() {
6980 dealloc(m_imp);
6981 }
6982
m() const6983 numeral_manager & manager::m() const {
6984 return m_imp->m_manager.m();
6985 }
6986
mm() const6987 monomial_manager & manager::mm() const {
6988 return m_imp->mm();
6989 }
6990
modular() const6991 bool manager::modular() const {
6992 return m_imp->m().modular();
6993 }
6994
p() const6995 numeral const & manager::p() const {
6996 return m_imp->m().p();
6997 }
6998
set_z()6999 void manager::set_z() {
7000 return m_imp->m().set_z();
7001 }
7002
set_zp(numeral const & p)7003 void manager::set_zp(numeral const & p) {
7004 return m_imp->m().set_zp(p);
7005 }
7006
set_zp(uint64_t p)7007 void manager::set_zp(uint64_t p) {
7008 return m_imp->m().set_zp(p);
7009 }
7010
is_var(polynomial const * p,var & v)7011 bool manager::is_var(polynomial const* p, var& v) {
7012 return p->size() == 1 && is_var(p->m(0), v) && m_imp->m().is_one(p->a(0));
7013 }
7014
is_var(monomial const * m,var & v)7015 bool manager::is_var(monomial const* m, var& v) {
7016 return m->size() == 1 && m->degree(0) == 1 && (v = m->get_var(0), true);
7017 }
7018
is_var_num(polynomial const * p,var & v,scoped_numeral & n)7019 bool manager::is_var_num(polynomial const* p, var& v, scoped_numeral& n) {
7020 return p->size() == 2 && m_imp->m().is_one(p->a(0)) && is_var(p->m(0), v) && is_unit(p->m(1)) && (n = p->a(1), true);
7021 }
7022
allocator() const7023 small_object_allocator & manager::allocator() const {
7024 return m_imp->mm().allocator();
7025 }
7026
add_del_eh(del_eh * eh)7027 void manager::add_del_eh(del_eh * eh) {
7028 m_imp->add_del_eh(eh);
7029 }
7030
remove_del_eh(del_eh * eh)7031 void manager::remove_del_eh(del_eh * eh) {
7032 m_imp->remove_del_eh(eh);
7033 }
7034
mk_var()7035 var manager::mk_var() {
7036 return m_imp->mk_var();
7037 }
7038
num_vars() const7039 unsigned manager::num_vars() const {
7040 return m_imp->num_vars();
7041 }
7042
inc_ref(monomial * m)7043 void manager::inc_ref(monomial * m) {
7044 if (m)
7045 m->inc_ref();
7046 }
7047
dec_ref(monomial * m)7048 void manager::dec_ref(monomial * m) {
7049 if (m)
7050 m_imp->dec_ref(m);
7051 }
7052
inc_ref(polynomial * p)7053 void manager::inc_ref(polynomial * p) {
7054 if (p)
7055 p->inc_ref();
7056 }
7057
dec_ref(polynomial * p)7058 void manager::dec_ref(polynomial * p) {
7059 if (p)
7060 m_imp->dec_ref(p);
7061 }
7062
lex_sort(polynomial * p)7063 void manager::lex_sort(polynomial * p) {
7064 m_imp->lex_sort(p);
7065 }
7066
hash(monomial const * m)7067 unsigned manager::hash(monomial const * m) {
7068 return m->hash();
7069 }
7070
hash(polynomial const * p)7071 unsigned manager::hash(polynomial const * p) {
7072 return m_imp->hash(p);
7073 }
7074
gcd_simplify(polynomial * p)7075 void manager::gcd_simplify(polynomial * p) {
7076 m_imp->gcd_simplify(p);
7077 }
7078
coeff(polynomial const * p,var x,unsigned k)7079 polynomial * manager::coeff(polynomial const * p, var x, unsigned k) {
7080 return m_imp->coeff(p, x, k);
7081 }
7082
coeff(polynomial const * p,var x,unsigned k,polynomial_ref & reduct)7083 polynomial * manager::coeff(polynomial const * p, var x, unsigned k, polynomial_ref & reduct) {
7084 return m_imp->coeff(p, x, k, reduct);
7085 }
7086
nonzero_const_coeff(polynomial const * p,var x,unsigned k)7087 bool manager::nonzero_const_coeff(polynomial const * p, var x, unsigned k) {
7088 return m_imp->nonzero_const_coeff(p, x, k);
7089 }
7090
const_coeff(polynomial const * p,var x,unsigned k,numeral & c)7091 bool manager::const_coeff(polynomial const * p, var x, unsigned k, numeral & c) {
7092 return m_imp->const_coeff(p, x, k, c);
7093 }
7094
mk_unit()7095 monomial * manager::mk_unit() {
7096 return m_imp->mk_unit();
7097 }
7098
mk_monomial(var x)7099 monomial * manager::mk_monomial(var x) {
7100 return m_imp->mk_monomial(x);
7101 }
7102
mk_monomial(var x,unsigned k)7103 monomial * manager::mk_monomial(var x, unsigned k) {
7104 return m_imp->mk_monomial(x, k);
7105 }
7106
mk_monomial(unsigned sz,var * xs)7107 monomial * manager::mk_monomial(unsigned sz, var * xs) {
7108 return m_imp->mk_monomial(sz, xs);
7109 }
7110
convert(monomial const * src)7111 monomial * manager::convert(monomial const * src) {
7112 return m_imp->convert(src);
7113 }
7114
mul(monomial const * m1,monomial const * m2)7115 monomial * manager::mul(monomial const * m1, monomial const * m2) {
7116 return m_imp->mul(m1, m2);
7117 }
7118
div(monomial const * m1,monomial const * m2)7119 bool manager::div(monomial const * m1, monomial const * m2) {
7120 return m_imp->div(m1, m2);
7121 }
7122
div(monomial const * m1,monomial const * m2,monomial_ref & r)7123 bool manager::div(monomial const * m1, monomial const * m2, monomial_ref & r) {
7124 return m_imp->div(m1, m2, r);
7125 }
7126
newton_interpolation(var x,unsigned d,numeral const * inputs,polynomial * const * outputs,polynomial_ref & r)7127 void manager::newton_interpolation(var x, unsigned d, numeral const * inputs, polynomial * const * outputs, polynomial_ref & r) {
7128 return m_imp->newton_interpolation(x, d, inputs, outputs, r);
7129 }
7130
gcd(monomial const * m1,monomial const * m2,monomial * & q1,monomial * & q2)7131 monomial * manager::gcd(monomial const * m1, monomial const * m2, monomial * & q1, monomial * & q2) {
7132 return m_imp->gcd(m1, m2, q1, q2);
7133 }
7134
unify(monomial const * m1,monomial const * m2,monomial * & q1,monomial * & q2)7135 bool manager::unify(monomial const * m1, monomial const * m2, monomial * & q1, monomial * & q2) {
7136 return m_imp->unify(m1, m2, q1, q2);
7137 }
7138
pw(monomial const * m,unsigned k)7139 monomial * manager::pw(monomial const * m, unsigned k) {
7140 return m_imp->pw(m, k);
7141 }
7142
mk_zero()7143 polynomial * manager::mk_zero() {
7144 return m_imp->mk_zero();
7145 }
7146
mk_const(numeral & a)7147 polynomial * manager::mk_const(numeral & a) {
7148 return m_imp->mk_const(a);
7149 }
7150
mk_const(rational const & a)7151 polynomial * manager::mk_const(rational const & a) {
7152 return m_imp->mk_const(a);
7153 }
7154
mk_polynomial(var x,unsigned k)7155 polynomial * manager::mk_polynomial(var x, unsigned k) {
7156 return m_imp->mk_polynomial(x, k);
7157 }
7158
mk_polynomial(unsigned sz,numeral * as,monomial * const * ms)7159 polynomial * manager::mk_polynomial(unsigned sz, numeral * as, monomial * const * ms) {
7160 return m_imp->mk_polynomial(sz, as, ms);
7161 }
7162
mk_polynomial(unsigned sz,rational const * as,monomial * const * ms)7163 polynomial * manager::mk_polynomial(unsigned sz, rational const * as, monomial * const * ms) {
7164 return m_imp->mk_polynomial(sz, as, ms);
7165 }
7166
mk_linear(unsigned sz,rational const * as,var const * xs,rational const & c)7167 polynomial * manager::mk_linear(unsigned sz, rational const * as, var const * xs, rational const & c) {
7168 return m_imp->mk_linear(sz, as, xs, c);
7169 }
7170
mk_linear(unsigned sz,numeral * as,var const * xs,numeral & c)7171 polynomial * manager::mk_linear(unsigned sz, numeral * as, var const * xs, numeral & c) {
7172 return m_imp->mk_linear(sz, as, xs, c);
7173 }
7174
mk_univariate(var x,unsigned n,numeral * as)7175 polynomial * manager::mk_univariate(var x, unsigned n, numeral * as) {
7176 return m_imp->mk_univariate(x, n, as);
7177 }
7178
neg(polynomial const * p)7179 polynomial * manager::neg(polynomial const * p) {
7180 return m_imp->neg(p);
7181 }
7182
add(polynomial const * p1,polynomial const * p2)7183 polynomial * manager::add(polynomial const * p1, polynomial const * p2) {
7184 return m_imp->add(p1, p2);
7185 }
7186
sub(polynomial const * p1,polynomial const * p2)7187 polynomial * manager::sub(polynomial const * p1, polynomial const * p2) {
7188 return m_imp->sub(p1, p2);
7189 }
7190
addmul(numeral const & a1,monomial const * m1,polynomial const * p1,numeral const & a2,monomial const * m2,polynomial const * p2)7191 polynomial * manager::addmul(numeral const & a1, monomial const * m1, polynomial const * p1,
7192 numeral const & a2, monomial const * m2, polynomial const * p2) {
7193 return m_imp->addmul(a1, m1, p1, a2, m2, p2);
7194 }
7195
addmul(polynomial const * p1,numeral const & a2,monomial const * m2,polynomial const * p2)7196 polynomial * manager::addmul(polynomial const * p1, numeral const & a2, monomial const * m2, polynomial const * p2) {
7197 return m_imp->addmul(p1, a2, m2, p2);
7198 }
7199
addmul(polynomial const * p1,numeral const & a2,polynomial const * p2)7200 polynomial * manager::addmul(polynomial const * p1, numeral const & a2, polynomial const * p2) {
7201 return m_imp->addmul(p1, a2, p2);
7202 }
7203
mul(numeral const & a,polynomial const * p)7204 polynomial * manager::mul(numeral const & a, polynomial const * p) {
7205 return m_imp->mul(a, p);
7206 }
7207
mul(rational const & a,polynomial const * p)7208 polynomial * manager::mul(rational const & a, polynomial const * p) {
7209 return m_imp->mul(a, p);
7210 }
7211
mul(polynomial const * p1,polynomial const * p2)7212 polynomial * manager::mul(polynomial const * p1, polynomial const * p2) {
7213 return m_imp->mul(p1, p2);
7214 }
7215
mul(numeral const & a,monomial const * m,polynomial const * p)7216 polynomial * manager::mul(numeral const & a, monomial const * m, polynomial const * p) {
7217 return m_imp->mul(a, m, p);
7218 }
7219
mul(monomial const * m,polynomial const * p)7220 polynomial * manager::mul(monomial const * m, polynomial const * p) {
7221 return m_imp->mul(m, p);
7222 }
7223
pw(polynomial const * p,unsigned k,polynomial_ref & r)7224 void manager::pw(polynomial const * p, unsigned k, polynomial_ref & r) {
7225 m_imp->pw(p, k, r);
7226 }
7227
int_content(polynomial const * p,numeral & c)7228 void manager::int_content(polynomial const * p, numeral & c) {
7229 m_imp->ic(p, c);
7230 }
7231
abs_norm(polynomial const * p,numeral & norm)7232 void manager::abs_norm(polynomial const * p, numeral & norm) {
7233 m_imp->abs_norm(p, norm);
7234 }
7235
numeral_lc(polynomial const * p,var x)7236 polynomial::numeral const & manager::numeral_lc(polynomial const * p, var x) {
7237 return m_imp->numeral_lc(p, x);
7238 }
7239
numeral_tc(polynomial const * p)7240 polynomial::numeral const & manager::numeral_tc(polynomial const * p) {
7241 return m_imp->numeral_tc(p);
7242 }
7243
content(polynomial const * p,var x,numeral & i,polynomial_ref & c)7244 void manager::content(polynomial const * p, var x, numeral & i, polynomial_ref & c) {
7245 polynomial_ref pp(*this);
7246 m_imp->iccp(p, x, i, c, pp);
7247 }
7248
content(polynomial const * p,var x,polynomial_ref & c)7249 void manager::content(polynomial const * p, var x, polynomial_ref & c) {
7250 scoped_numeral i(m_imp->m_manager.m());
7251 content(p, x, i, c);
7252 if (!m_imp->m_manager.is_one(i)) {
7253 c = mul(i, c);
7254 }
7255 }
7256
primitive(polynomial const * p,var x,polynomial_ref & pp)7257 void manager::primitive(polynomial const * p, var x, polynomial_ref & pp) {
7258 pp = m_imp->pp(p, x);
7259 }
7260
icpp(polynomial const * p,var x,numeral & i,polynomial_ref & c,polynomial_ref & pp)7261 void manager::icpp(polynomial const * p, var x, numeral & i, polynomial_ref & c, polynomial_ref & pp) {
7262 m_imp->iccp(p, x, i, c, pp);
7263 }
7264
flip_sign_if_lm_neg(polynomial const * p)7265 polynomial * manager::flip_sign_if_lm_neg(polynomial const * p) {
7266 return m_imp->flip_sign_if_lm_neg_core(p);
7267 }
7268
gcd(polynomial const * p,polynomial const * q,polynomial_ref & g)7269 void manager::gcd(polynomial const * p, polynomial const * q, polynomial_ref & g) {
7270 m_imp->gcd(p, q, g);
7271 }
7272
derivative(polynomial const * p,var x)7273 polynomial * manager::derivative(polynomial const * p, var x) {
7274 return m_imp->derivative(p, x);
7275 }
7276
square_free(polynomial const * p,var x,polynomial_ref & r)7277 void manager::square_free(polynomial const * p, var x, polynomial_ref & r) {
7278 m_imp->square_free(p, x, r);
7279 }
7280
is_square_free(polynomial const * p,var x)7281 bool manager::is_square_free(polynomial const * p, var x) {
7282 return m_imp->is_square_free(p, x);
7283 }
7284
square_free(polynomial const * p,polynomial_ref & r)7285 void manager::square_free(polynomial const * p, polynomial_ref & r) {
7286 m_imp->square_free(p, r);
7287 }
7288
is_square_free(polynomial const * p)7289 bool manager::is_square_free(polynomial const * p) {
7290 return m_imp->is_square_free(p);
7291 }
7292
eq(polynomial const * p1,polynomial const * p2)7293 bool manager::eq(polynomial const * p1, polynomial const * p2) {
7294 return m_imp->eq(p1, p2);
7295 }
7296
compose_y(polynomial const * p,var y)7297 polynomial * manager::compose_y(polynomial const * p, var y) {
7298 return m_imp->compose_y(p, y);
7299 }
7300
compose_minus_x(polynomial const * p)7301 polynomial * manager::compose_minus_x(polynomial const * p) {
7302 return m_imp->compose_minus_x(p);
7303 }
7304
compose_1_div_x(polynomial const * p)7305 polynomial * manager::compose_1_div_x(polynomial const * p) {
7306 return m_imp->compose_1_div_x(p);
7307 }
7308
compose_x_div_y(polynomial const * p,var y)7309 polynomial * manager::compose_x_div_y(polynomial const * p, var y) {
7310 return m_imp->compose_x_div_y(p, y);
7311 }
7312
compose(polynomial const * p,polynomial const * q,polynomial_ref & r)7313 void manager::compose(polynomial const * p, polynomial const * q, polynomial_ref & r) {
7314 m_imp->compose(p, q, r);
7315 }
7316
compose_x_minus_y(polynomial const * p,var y,polynomial_ref & r)7317 void manager::compose_x_minus_y(polynomial const * p, var y, polynomial_ref & r) {
7318 m_imp->compose_x_minus_y(p, y, r);
7319 }
7320
compose_x_plus_y(polynomial const * p,var y,polynomial_ref & r)7321 void manager::compose_x_plus_y(polynomial const * p, var y, polynomial_ref & r) {
7322 m_imp->compose_x_plus_y(p, y, r);
7323 }
7324
compose_x_minus_c(polynomial const * p,numeral const & c,polynomial_ref & r)7325 void manager::compose_x_minus_c(polynomial const * p, numeral const & c, polynomial_ref & r) {
7326 m_imp->compose_x_minus_c(p, c, r);
7327 }
7328
sqrt(polynomial const * p,polynomial_ref & r)7329 bool manager::sqrt(polynomial const * p, polynomial_ref & r) {
7330 return m_imp->sqrt(p, r);
7331 }
7332
normalize(polynomial const * p)7333 polynomial * manager::normalize(polynomial const * p) {
7334 return m_imp->normalize(p);
7335 }
7336
exact_pseudo_remainder(polynomial const * p,polynomial const * q,var x,polynomial_ref & R)7337 void manager::exact_pseudo_remainder(polynomial const * p, polynomial const * q, var x, polynomial_ref & R) {
7338 m_imp->exact_pseudo_remainder(p, q, x, R);
7339 }
7340
pseudo_remainder(polynomial const * p,polynomial const * q,var x,unsigned & d,polynomial_ref & R)7341 void manager::pseudo_remainder(polynomial const * p, polynomial const * q, var x, unsigned & d, polynomial_ref & R) {
7342 m_imp->pseudo_remainder(p, q, x, d, R);
7343 }
7344
exact_pseudo_division(polynomial const * p,polynomial const * q,var x,polynomial_ref & Q,polynomial_ref & R)7345 void manager::exact_pseudo_division(polynomial const * p, polynomial const * q, var x, polynomial_ref & Q, polynomial_ref & R) {
7346 m_imp->exact_pseudo_division(p, q, x, Q, R);
7347 }
7348
pseudo_division(polynomial const * p,polynomial const * q,var x,unsigned & d,polynomial_ref & Q,polynomial_ref & R)7349 void manager::pseudo_division(polynomial const * p, polynomial const * q, var x, unsigned & d, polynomial_ref & Q, polynomial_ref & R) {
7350 m_imp->pseudo_division(p, q, x, d, Q, R);
7351 }
7352
exact_div(polynomial const * p,polynomial const * q)7353 polynomial * manager::exact_div(polynomial const * p, polynomial const * q) {
7354 return m_imp->exact_div(p, q);
7355 }
7356
exact_div(polynomial const * p,numeral const & c)7357 polynomial * manager::exact_div(polynomial const * p, numeral const & c) {
7358 return m_imp->exact_div(p, c);
7359 }
7360
divides(polynomial const * q,polynomial const * p)7361 bool manager::divides(polynomial const * q, polynomial const * p) {
7362 return m_imp->divides(q, p);
7363 }
7364
quasi_resultant(polynomial const * p,polynomial const * q,var x,polynomial_ref & r)7365 void manager::quasi_resultant(polynomial const * p, polynomial const * q, var x, polynomial_ref & r) {
7366 m_imp->quasi_resultant(p, q, x, r);
7367 }
7368
resultant(polynomial const * p,polynomial const * q,var x,polynomial_ref & r)7369 void manager::resultant(polynomial const * p, polynomial const * q, var x, polynomial_ref & r) {
7370 m_imp->resultant(p, q, x, r);
7371 }
7372
discriminant(polynomial const * p,var x,polynomial_ref & r)7373 void manager::discriminant(polynomial const * p, var x, polynomial_ref & r) {
7374 m_imp->discriminant(p, x, r);
7375 }
7376
psc_chain(polynomial const * p,polynomial const * q,var x,polynomial_ref_vector & S)7377 void manager::psc_chain(polynomial const * p, polynomial const * q, var x, polynomial_ref_vector & S) {
7378 m_imp->psc_chain(p, q, x, S);
7379 }
7380
sign(polynomial const * p,svector<lbool> const & sign_of_vars)7381 lbool manager::sign(polynomial const * p, svector<lbool> const& sign_of_vars) {
7382 return m_imp->sign(p, sign_of_vars);
7383 }
7384
is_pos(polynomial const * p)7385 bool manager::is_pos(polynomial const * p) {
7386 return m_imp->is_pos(p);
7387 }
7388
is_neg(polynomial const * p)7389 bool manager::is_neg(polynomial const * p) {
7390 return m_imp->is_neg(p);
7391 }
7392
is_nonpos(polynomial const * p)7393 bool manager::is_nonpos(polynomial const * p) {
7394 return m_imp->is_nonpos(p);
7395 }
7396
is_nonneg(polynomial const * p)7397 bool manager::is_nonneg(polynomial const * p) {
7398 return m_imp->is_nonneg(p);
7399 }
7400
rename(unsigned sz,var const * xs)7401 void manager::rename(unsigned sz, var const * xs) {
7402 return m_imp->rename(sz, xs);
7403 }
7404
vars(polynomial const * p,var_vector & xs)7405 void manager::vars(polynomial const * p, var_vector & xs) {
7406 m_imp->vars(p, xs);
7407 }
7408
substitute(polynomial const * p,var2mpq const & x2v)7409 polynomial * manager::substitute(polynomial const * p, var2mpq const & x2v) {
7410 return m_imp->substitute(p, x2v);
7411 }
7412
substitute(polynomial const * p,unsigned xs_sz,var const * xs,mpq const * vs)7413 polynomial * manager::substitute(polynomial const * p, unsigned xs_sz, var const * xs, mpq const * vs) {
7414 return m_imp->substitute(p, xs_sz, xs, vs);
7415 }
7416
substitute(polynomial const * p,unsigned xs_sz,var const * xs,numeral const * vs)7417 polynomial * manager::substitute(polynomial const * p, unsigned xs_sz, var const * xs, numeral const * vs) {
7418 return m_imp->substitute(p, xs_sz, xs, vs);
7419 }
7420
substitute(polynomial const * r,var x,polynomial const * p,polynomial const * q,polynomial_ref & result)7421 void manager::substitute(polynomial const* r, var x, polynomial const* p, polynomial const* q, polynomial_ref& result) {
7422 m_imp->substitute(r, x, p, q, result);
7423 }
7424
factor(polynomial const * p,factors & r,factor_params const & params)7425 void manager::factor(polynomial const * p, factors & r, factor_params const & params) {
7426 m_imp->factor(p, r, params);
7427 }
7428
to_polynomial(unsigned sz,numeral const * p,var x)7429 polynomial * manager::to_polynomial(unsigned sz, numeral const * p, var x) {
7430 return m_imp->to_polynomial(sz, p, x);
7431 }
7432
mk_glex_monic(polynomial const * p)7433 polynomial * manager::mk_glex_monic(polynomial const * p) {
7434 return m_imp->mk_glex_monic(p);
7435 }
7436
translate(polynomial const * p,var x,numeral const & v)7437 polynomial * manager::translate(polynomial const * p, var x, numeral const & v) {
7438 return m_imp->translate(p, x, v);
7439 }
7440
translate(polynomial const * p,unsigned xs_sz,var const * xs,numeral const * vs,polynomial_ref & r)7441 void manager::translate(polynomial const * p, unsigned xs_sz, var const * xs, numeral const * vs, polynomial_ref & r) {
7442 return m_imp->translate(p, xs_sz, xs, vs, r);
7443 }
7444
mod_d(polynomial const * p,var2degree const & x2d)7445 polynomial * manager::mod_d(polynomial const * p, var2degree const & x2d) {
7446 return m_imp->mod_d(p, x2d);
7447 }
7448
exact_pseudo_division_mod_d(polynomial const * p,polynomial const * q,var x,var2degree const & x2d,polynomial_ref & Q,polynomial_ref & R)7449 void manager::exact_pseudo_division_mod_d(polynomial const * p, polynomial const * q, var x, var2degree const & x2d,
7450 polynomial_ref & Q, polynomial_ref & R) {
7451 m_imp->exact_pseudo_division_mod_d(p, q, x, x2d, Q, R);
7452 }
7453
eval(polynomial const * p,var2mpbqi const & x2v,mpbqi & r)7454 void manager::eval(polynomial const * p, var2mpbqi const & x2v, mpbqi & r) {
7455 return m_imp->eval(p, x2v, r);
7456 }
7457
eval(polynomial const * p,var2mpq const & x2v,mpq & r)7458 void manager::eval(polynomial const * p, var2mpq const & x2v, mpq & r) {
7459 return m_imp->eval(p, x2v, r);
7460 }
7461
eval(polynomial const * p,var2anum const & x2v,algebraic_numbers::anum & r)7462 void manager::eval(polynomial const * p, var2anum const & x2v, algebraic_numbers::anum & r) {
7463 return m_imp->eval(p, x2v, r);
7464 }
7465
display(std::ostream & out,monomial const * m,display_var_proc const & proc,bool user_star) const7466 void manager::display(std::ostream & out, monomial const * m, display_var_proc const & proc, bool user_star) const {
7467 m->display(out, proc, user_star);
7468 }
7469
display(std::ostream & out,polynomial const * p,display_var_proc const & proc,bool use_star) const7470 void manager::display(std::ostream & out, polynomial const * p, display_var_proc const & proc, bool use_star) const {
7471 SASSERT(m_imp->consistent_coeffs(p));
7472 p->display(out, m_imp->m_manager, proc, use_star);
7473 }
7474
display_smt2(std::ostream & out,polynomial const * p,display_var_proc const & proc) const7475 void manager::display_smt2(std::ostream & out, polynomial const * p, display_var_proc const & proc) const {
7476 p->display_smt2(out, m_imp->m_manager, proc);
7477 }
7478 };
7479
convert(polynomial::manager & sm,polynomial::polynomial * p,polynomial::manager & tm,polynomial::var x,unsigned max_d)7480 polynomial::polynomial * convert(polynomial::manager & sm, polynomial::polynomial * p, polynomial::manager & tm,
7481 polynomial::var x, unsigned max_d) {
7482 ptr_buffer<polynomial::monomial, 128> ms;
7483 polynomial::numeral_manager & nm = tm.m();
7484 _scoped_numeral_buffer<polynomial::numeral_manager, 128> as(nm);
7485 unsigned sz = sm.size(p);
7486 if (&sm == &tm) {
7487 // same source and target manager.
7488 // So, we just return p
7489 return p;
7490 }
7491 else if (&(sm.mm()) == &(tm.mm())) {
7492 // polynomial managers share the same monomial manager.
7493 // So, we don't need to convert monomials.
7494 for (unsigned i = 0; i < sz; i++) {
7495 polynomial::monomial * m = sm.get_monomial(p, i);
7496 if (x == polynomial::null_var || sm.degree_of(m, x) <= max_d) {
7497 ms.push_back(m);
7498 as.push_back(polynomial::numeral());
7499 nm.set(as.back(), sm.coeff(p, i));
7500 }
7501 }
7502 }
7503 else {
7504 for (unsigned i = 0; i < sz; i++) {
7505 polynomial::monomial * m = sm.get_monomial(p, i);
7506 if (x == polynomial::null_var || sm.degree_of(m, x) <= max_d) {
7507 ms.push_back(tm.convert(m));
7508 as.push_back(polynomial::numeral());
7509 nm.set(as.back(), sm.coeff(p, i));
7510 }
7511 }
7512 }
7513 return tm.mk_polynomial(as.size(), as.data(), ms.data());
7514 }
7515
operator <<(std::ostream & out,polynomial_ref_vector const & seq)7516 std::ostream & operator<<(std::ostream & out, polynomial_ref_vector const & seq) {
7517 unsigned sz = seq.size();
7518 for (unsigned i = 0; i < sz; i++) {
7519 seq.m().display(out, seq.get(i));
7520 out << "\n";
7521 }
7522 return out;
7523 }
7524