GiNaC 1.8.10
normal.cpp
Go to the documentation of this file.
1
8/*
9 * GiNaC Copyright (C) 1999-2026 Johannes Gutenberg University Mainz, Germany
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program. If not, see <https://www.gnu.org/licenses/>.
23 */
24
25#include "normal.h"
26#include "basic.h"
27#include "ex.h"
28#include "add.h"
29#include "constant.h"
30#include "expairseq.h"
31#include "fail.h"
32#include "inifcns.h"
33#include "lst.h"
34#include "mul.h"
35#include "numeric.h"
36#include "power.h"
37#include "relational.h"
38#include "operators.h"
39#include "matrix.h"
40#include "pseries.h"
41#include "symbol.h"
42#include "utils.h"
43#include "polynomial/chinrem_gcd.h"
44
45#include <algorithm>
46#include <map>
47
48namespace GiNaC {
49
50// If comparing expressions (ex::compare()) is fast, you can set this to 1.
51// Some routines like quo(), rem() and gcd() will then return a quick answer
52// when they are called with two identical arguments.
53#define FAST_COMPARE 1
54
55// Set this if you want divide_in_z() to use remembering
56#define USE_REMEMBER 0
57
58// Set this if you want divide_in_z() to use trial division followed by
59// polynomial interpolation (always slower except for completely dense
60// polynomials)
61#define USE_TRIAL_DIVISION 0
62
63// Set this to enable some statistical output for the GCD routines
64#define STATISTICS 0
65
66
67#if STATISTICS
68// Statistics variables
69static int gcd_called = 0;
70static int sr_gcd_called = 0;
71static int heur_gcd_called = 0;
72static int heur_gcd_failed = 0;
73
74// Print statistics at end of program
75static struct _stat_print {
76 _stat_print() {}
77 ~_stat_print() {
78 std::cout << "gcd() called " << gcd_called << " times\n";
79 std::cout << "sr_gcd() called " << sr_gcd_called << " times\n";
80 std::cout << "heur_gcd() called " << heur_gcd_called << " times\n";
81 std::cout << "heur_gcd() failed " << heur_gcd_failed << " times\n";
82 }
83} stat_print;
84#endif
85
86
94static bool get_first_symbol(const ex &e, ex &x)
95{
96 if (is_a<symbol>(e)) {
97 x = e;
98 return true;
99 } else if (is_exactly_a<add>(e) || is_exactly_a<mul>(e)) {
100 for (size_t i=0; i<e.nops(); i++)
101 if (get_first_symbol(e.op(i), x))
102 return true;
103 } else if (is_exactly_a<power>(e)) {
104 if (get_first_symbol(e.op(0), x))
105 return true;
106 }
107 return false;
108}
109
110
111/*
112 * Statistical information about symbols in polynomials
113 */
114
121struct sym_desc {
123 sym_desc(const ex& s)
124 : sym(s), deg_a(0), deg_b(0), ldeg_a(0), ldeg_b(0), max_deg(0), max_lcnops(0)
125 { }
126
129
131 int deg_a;
132
134 int deg_b;
135
138
141
144
147
149 bool operator<(const sym_desc &x) const
150 {
151 if (max_deg == x.max_deg)
152 return max_lcnops < x.max_lcnops;
153 else
154 return max_deg < x.max_deg;
155 }
156};
157
158// Vector of sym_desc structures
159typedef std::vector<sym_desc> sym_desc_vec;
160
161// Add symbol the sym_desc_vec (used internally by get_symbol_stats())
162static void add_symbol(const ex &s, sym_desc_vec &v)
163{
164 for (auto & it : v)
165 if (it.sym.is_equal(s)) // If it's already in there, don't add it a second time
166 return;
167
168 v.push_back(sym_desc(s));
169}
170
171// Collect all symbols of an expression (used internally by get_symbol_stats())
172static void collect_symbols(const ex &e, sym_desc_vec &v)
173{
174 if (is_a<symbol>(e)) {
175 add_symbol(e, v);
176 } else if (is_exactly_a<add>(e) || is_exactly_a<mul>(e)) {
177 for (size_t i=0; i<e.nops(); i++)
178 collect_symbols(e.op(i), v);
179 } else if (is_exactly_a<power>(e)) {
180 collect_symbols(e.op(0), v);
181 }
182}
183
196static void get_symbol_stats(const ex &a, const ex &b, sym_desc_vec &v)
197{
198 collect_symbols(a, v);
199 collect_symbols(b, v);
200 for (auto & it : v) {
201 int deg_a = a.degree(it.sym);
202 int deg_b = b.degree(it.sym);
203 it.deg_a = deg_a;
204 it.deg_b = deg_b;
205 it.max_deg = std::max(deg_a, deg_b);
206 it.max_lcnops = std::max(a.lcoeff(it.sym).nops(), b.lcoeff(it.sym).nops());
207 it.ldeg_a = a.ldegree(it.sym);
208 it.ldeg_b = b.ldegree(it.sym);
209 }
210 std::sort(v.begin(), v.end());
211
212#if 0
213 std::clog << "Symbols:\n";
214 auto it = v.begin(), itend = v.end();
215 while (it != itend) {
216 std::clog << " " << it->sym << ": deg_a=" << it->deg_a << ", deg_b=" << it->deg_b << ", ldeg_a=" << it->ldeg_a << ", ldeg_b=" << it->ldeg_b << ", max_deg=" << it->max_deg << ", max_lcnops=" << it->max_lcnops << std::endl;
217 std::clog << " lcoeff_a=" << a.lcoeff(it->sym) << ", lcoeff_b=" << b.lcoeff(it->sym) << std::endl;
218 ++it;
219 }
220#endif
221}
222
223
224/*
225 * Computation of LCM of denominators of coefficients of a polynomial
226 */
227
228// Compute LCM of denominators of coefficients by going through the
229// expression recursively (used internally by lcm_of_coefficients_denominators())
230static numeric lcmcoeff(const ex &e, const numeric &l)
231{
233 return lcm(ex_to<numeric>(e).denom(), l);
234 else if (is_exactly_a<add>(e)) {
235 numeric c = *_num1_p;
236 for (size_t i=0; i<e.nops(); i++)
237 c = lcmcoeff(e.op(i), c);
238 return lcm(c, l);
239 } else if (is_exactly_a<mul>(e)) {
240 numeric c = *_num1_p;
241 for (size_t i=0; i<e.nops(); i++)
242 c *= lcmcoeff(e.op(i), *_num1_p);
243 return lcm(c, l);
244 } else if (is_exactly_a<power>(e)) {
245 if (is_a<symbol>(e.op(0)))
246 return l;
247 else
248 return pow(lcmcoeff(e.op(0), l), ex_to<numeric>(e.op(1)));
249 }
250 return l;
251}
252
261{
262 return lcmcoeff(e, *_num1_p);
263}
264
270static ex multiply_lcm(const ex &e, const numeric &lcm)
271{
272 if (lcm.is_equal(*_num1_p))
273 // e * 1 -> e;
274 return e;
275
276 if (is_exactly_a<mul>(e)) {
277 // (a*b*...)*lcm -> (a*lcma)*(b*lcmb)*...*(lcm/(lcma*lcmb*...))
278 size_t num = e.nops();
279 exvector v;
280 v.reserve(num + 1);
281 numeric lcm_accum = *_num1_p;
282 for (size_t i=0; i<num; i++) {
283 numeric op_lcm = lcmcoeff(e.op(i), *_num1_p);
284 v.push_back(multiply_lcm(e.op(i), op_lcm));
285 lcm_accum *= op_lcm;
286 }
287 v.push_back(lcm / lcm_accum);
288 return dynallocate<mul>(v);
289 } else if (is_exactly_a<add>(e)) {
290 // (a+b+...)*lcm -> a*lcm+b*lcm+...
291 size_t num = e.nops();
292 exvector v;
293 v.reserve(num);
294 for (size_t i=0; i<num; i++)
295 v.push_back(multiply_lcm(e.op(i), lcm));
296 return dynallocate<add>(v);
297 } else if (is_exactly_a<power>(e)) {
298 if (!is_a<symbol>(e.op(0))) {
299 // (b^e)*lcm -> (b*lcm^(1/e))^e if lcm^(1/e) ∈ ℚ (i.e. not a float)
300 // but not for symbolic b, as evaluation would undo this again
301 numeric root_of_lcm = lcm.power(ex_to<numeric>(e.op(1)).inverse());
302 if (root_of_lcm.is_rational())
303 return pow(multiply_lcm(e.op(0), root_of_lcm), e.op(1));
304 }
305 }
306 // can't recurse down into e
307 return dynallocate<mul>(e, lcm);
308}
309
310
318{
319 return bp->integer_content();
320}
321
323{
324 return *_num1_p;
325}
326
328{
329 return abs(*this);
330}
331
333{
334 numeric c = *_num0_p, l = *_num1_p;
335 for (auto & it : seq) {
336 GINAC_ASSERT(!is_exactly_a<numeric>(it.rest));
337 GINAC_ASSERT(is_exactly_a<numeric>(it.coeff));
338 c = gcd(ex_to<numeric>(it.coeff).numer(), c);
339 l = lcm(ex_to<numeric>(it.coeff).denom(), l);
340 }
341 GINAC_ASSERT(is_exactly_a<numeric>(overall_coeff));
342 c = gcd(ex_to<numeric>(overall_coeff).numer(), c);
343 l = lcm(ex_to<numeric>(overall_coeff).denom(), l);
344 return c/l;
345}
346
348{
349#ifdef DO_GINAC_ASSERT
350 for (auto & it : seq) {
351 GINAC_ASSERT(!is_exactly_a<numeric>(recombine_pair_to_ex(it)));
352 }
353#endif // def DO_GINAC_ASSERT
354 GINAC_ASSERT(is_exactly_a<numeric>(overall_coeff));
355 return abs(ex_to<numeric>(overall_coeff));
356}
357
358
359/*
360 * Polynomial quotients and remainders
361 */
362
372ex quo(const ex &a, const ex &b, const ex &x, bool check_args)
373{
374 if (b.is_zero())
375 throw(std::overflow_error("quo: division by zero"));
376 if (is_exactly_a<numeric>(a) && is_exactly_a<numeric>(b))
377 return a / b;
378#if FAST_COMPARE
379 if (a.is_equal(b))
380 return _ex1;
381#endif
383 throw(std::invalid_argument("quo: arguments must be polynomials over the rationals"));
384
385 // Polynomial long division
386 ex r = a.expand();
387 if (r.is_zero())
388 return r;
389 int bdeg = b.degree(x);
390 int rdeg = r.degree(x);
391 ex blcoeff = b.expand().coeff(x, bdeg);
392 bool blcoeff_is_numeric = is_exactly_a<numeric>(blcoeff);
393 exvector v; v.reserve(std::max(rdeg - bdeg + 1, 0));
394 while (rdeg >= bdeg) {
395 ex term, rcoeff = r.coeff(x, rdeg);
396 if (blcoeff_is_numeric)
397 term = rcoeff / blcoeff;
398 else {
399 if (!divide(rcoeff, blcoeff, term, false))
400 return dynallocate<fail>();
401 }
402 term *= pow(x, rdeg - bdeg);
403 v.push_back(term);
404 r -= (term * b).expand();
405 if (r.is_zero())
406 break;
407 rdeg = r.degree(x);
408 }
409 return dynallocate<add>(v);
410}
411
412
422ex rem(const ex &a, const ex &b, const ex &x, bool check_args)
423{
424 if (b.is_zero())
425 throw(std::overflow_error("rem: division by zero"));
426 if (is_exactly_a<numeric>(a)) {
427 if (is_exactly_a<numeric>(b))
428 return _ex0;
429 else
430 return a;
431 }
432#if FAST_COMPARE
433 if (a.is_equal(b))
434 return _ex0;
435#endif
437 throw(std::invalid_argument("rem: arguments must be polynomials over the rationals"));
438
439 // Polynomial long division
440 ex r = a.expand();
441 if (r.is_zero())
442 return r;
443 int bdeg = b.degree(x);
444 int rdeg = r.degree(x);
445 ex blcoeff = b.expand().coeff(x, bdeg);
446 bool blcoeff_is_numeric = is_exactly_a<numeric>(blcoeff);
447 while (rdeg >= bdeg) {
448 ex term, rcoeff = r.coeff(x, rdeg);
449 if (blcoeff_is_numeric)
450 term = rcoeff / blcoeff;
451 else {
452 if (!divide(rcoeff, blcoeff, term, false))
453 return dynallocate<fail>();
454 }
455 term *= pow(x, rdeg - bdeg);
456 r -= (term * b).expand();
457 if (r.is_zero())
458 break;
459 rdeg = r.degree(x);
460 }
461 return r;
462}
463
464
471ex decomp_rational(const ex &a, const ex &x)
472{
473 ex nd = numer_denom(a);
474 ex numer = nd.op(0), denom = nd.op(1);
475 ex q = quo(numer, denom, x);
476 if (is_exactly_a<fail>(q))
477 return a;
478 else
479 return q + rem(numer, denom, x) / denom;
480}
481
482
491ex prem(const ex &a, const ex &b, const ex &x, bool check_args)
492{
493 if (b.is_zero())
494 throw(std::overflow_error("prem: division by zero"));
495 if (is_exactly_a<numeric>(a)) {
496 if (is_exactly_a<numeric>(b))
497 return _ex0;
498 else
499 return b;
500 }
502 throw(std::invalid_argument("prem: arguments must be polynomials over the rationals"));
503
504 // Polynomial long division
505 ex r = a.expand();
506 ex eb = b.expand();
507 int rdeg = r.degree(x);
508 int bdeg = eb.degree(x);
509 ex blcoeff;
510 if (bdeg <= rdeg) {
511 blcoeff = eb.coeff(x, bdeg);
512 if (bdeg == 0)
513 eb = _ex0;
514 else
515 eb -= blcoeff * pow(x, bdeg);
516 } else
517 blcoeff = _ex1;
518
519 int delta = rdeg - bdeg + 1, i = 0;
520 while (rdeg >= bdeg && !r.is_zero()) {
521 ex rlcoeff = r.coeff(x, rdeg);
522 ex term = (pow(x, rdeg - bdeg) * eb * rlcoeff).expand();
523 if (rdeg == 0)
524 r = _ex0;
525 else
526 r -= rlcoeff * pow(x, rdeg);
527 r = (blcoeff * r).expand() - term;
528 rdeg = r.degree(x);
529 i++;
530 }
531 return pow(blcoeff, delta - i) * r;
532}
533
534
543ex sprem(const ex &a, const ex &b, const ex &x, bool check_args)
544{
545 if (b.is_zero())
546 throw(std::overflow_error("prem: division by zero"));
547 if (is_exactly_a<numeric>(a)) {
548 if (is_exactly_a<numeric>(b))
549 return _ex0;
550 else
551 return b;
552 }
554 throw(std::invalid_argument("prem: arguments must be polynomials over the rationals"));
555
556 // Polynomial long division
557 ex r = a.expand();
558 ex eb = b.expand();
559 int rdeg = r.degree(x);
560 int bdeg = eb.degree(x);
561 ex blcoeff;
562 if (bdeg <= rdeg) {
563 blcoeff = eb.coeff(x, bdeg);
564 if (bdeg == 0)
565 eb = _ex0;
566 else
567 eb -= blcoeff * pow(x, bdeg);
568 } else
569 blcoeff = _ex1;
570
571 while (rdeg >= bdeg && !r.is_zero()) {
572 ex rlcoeff = r.coeff(x, rdeg);
573 ex term = (pow(x, rdeg - bdeg) * eb * rlcoeff).expand();
574 if (rdeg == 0)
575 r = _ex0;
576 else
577 r -= rlcoeff * pow(x, rdeg);
578 r = (blcoeff * r).expand() - term;
579 rdeg = r.degree(x);
580 }
581 return r;
582}
583
584
594bool divide(const ex &a, const ex &b, ex &q, bool check_args)
595{
596 if (b.is_zero())
597 throw(std::overflow_error("divide: division by zero"));
598 if (a.is_zero()) {
599 q = _ex0;
600 return true;
601 }
602 if (is_exactly_a<numeric>(b)) {
603 q = a / b;
604 return true;
605 } else if (is_exactly_a<numeric>(a))
606 return false;
607#if FAST_COMPARE
608 if (a.is_equal(b)) {
609 q = _ex1;
610 return true;
611 }
612#endif
613 if (check_args && (!a.info(info_flags::rational_polynomial) ||
615 throw(std::invalid_argument("divide: arguments must be polynomials over the rationals"));
616
617 // Find first symbol
618 ex x;
619 if (!get_first_symbol(a, x) && !get_first_symbol(b, x))
620 throw(std::invalid_argument("invalid expression in divide()"));
621
622 // Try to avoid expanding partially factored expressions.
623 if (is_exactly_a<mul>(b)) {
624 // Divide sequentially by each term
625 ex rem_new, rem_old = a;
626 for (size_t i=0; i < b.nops(); i++) {
627 if (! divide(rem_old, b.op(i), rem_new, false))
628 return false;
629 rem_old = rem_new;
630 }
631 q = rem_new;
632 return true;
633 } else if (is_exactly_a<power>(b)) {
634 const ex& bb(b.op(0));
635 int exp_b = ex_to<numeric>(b.op(1)).to_int();
636 ex rem_new, rem_old = a;
637 for (int i=exp_b; i>0; i--) {
638 if (! divide(rem_old, bb, rem_new, false))
639 return false;
640 rem_old = rem_new;
641 }
642 q = rem_new;
643 return true;
644 }
645
646 if (is_exactly_a<mul>(a)) {
647 // Divide sequentially each term. If some term in a is divisible
648 // by b we are done... and if not, we can't really say anything.
649 size_t i;
650 ex rem_i;
651 bool divisible_p = false;
652 for (i=0; i < a.nops(); ++i) {
653 if (divide(a.op(i), b, rem_i, false)) {
654 divisible_p = true;
655 break;
656 }
657 }
658 if (divisible_p) {
659 exvector resv;
660 resv.reserve(a.nops());
661 for (size_t j=0; j < a.nops(); j++) {
662 if (j==i)
663 resv.push_back(rem_i);
664 else
665 resv.push_back(a.op(j));
666 }
667 q = dynallocate<mul>(resv);
668 return true;
669 }
670 } else if (is_exactly_a<power>(a)) {
671 // The base itself might be divisible by b, in that case we don't
672 // need to expand a
673 const ex& ab(a.op(0));
674 int a_exp = ex_to<numeric>(a.op(1)).to_int();
675 ex rem_i;
676 if (divide(ab, b, rem_i, false)) {
677 q = rem_i * pow(ab, a_exp - 1);
678 return true;
679 }
680// code below is commented-out because it leads to a significant slowdown
681// for (int i=2; i < a_exp; i++) {
682// if (divide(power(ab, i), b, rem_i, false)) {
683// q = rem_i*power(ab, a_exp - i);
684// return true;
685// }
686// } // ... so we *really* need to expand expression.
687 }
688
689 // Polynomial long division (recursive)
690 ex r = a.expand();
691 if (r.is_zero()) {
692 q = _ex0;
693 return true;
694 }
695 int bdeg = b.degree(x);
696 int rdeg = r.degree(x);
697 ex blcoeff = b.expand().coeff(x, bdeg);
698 bool blcoeff_is_numeric = is_exactly_a<numeric>(blcoeff);
699 exvector v; v.reserve(std::max(rdeg - bdeg + 1, 0));
700 while (rdeg >= bdeg) {
701 ex term, rcoeff = r.coeff(x, rdeg);
702 if (blcoeff_is_numeric)
703 term = rcoeff / blcoeff;
704 else
705 if (!divide(rcoeff, blcoeff, term, false))
706 return false;
707 term *= pow(x, rdeg - bdeg);
708 v.push_back(term);
709 r -= (term * b).expand();
710 if (r.is_zero()) {
711 q = dynallocate<add>(v);
712 return true;
713 }
714 rdeg = r.degree(x);
715 }
716 return false;
717}
718
719
720#if USE_REMEMBER
721/*
722 * Remembering
723 */
724
725typedef std::pair<ex, ex> ex2;
726typedef std::pair<ex, bool> exbool;
727
728struct ex2_less {
729 bool operator() (const ex2 &p, const ex2 &q) const
730 {
731 int cmp = p.first.compare(q.first);
732 return ((cmp<0) || (!(cmp>0) && p.second.compare(q.second)<0));
733 }
734};
735
736typedef std::map<ex2, exbool, ex2_less> ex2_exbool_remember;
737#endif
738
739
756static bool divide_in_z(const ex &a, const ex &b, ex &q, sym_desc_vec::const_iterator var)
757{
758 q = _ex0;
759 if (b.is_zero())
760 throw(std::overflow_error("divide_in_z: division by zero"));
761 if (b.is_equal(_ex1)) {
762 q = a;
763 return true;
764 }
765 if (is_exactly_a<numeric>(a)) {
766 if (is_exactly_a<numeric>(b)) {
767 q = a / b;
768 return q.info(info_flags::integer);
769 } else
770 return false;
771 }
772#if FAST_COMPARE
773 if (a.is_equal(b)) {
774 q = _ex1;
775 return true;
776 }
777#endif
778
779#if USE_REMEMBER
780 // Remembering
781 static ex2_exbool_remember dr_remember;
782 ex2_exbool_remember::const_iterator remembered = dr_remember.find(ex2(a, b));
783 if (remembered != dr_remember.end()) {
784 q = remembered->second.first;
785 return remembered->second.second;
786 }
787#endif
788
789 if (is_exactly_a<power>(b)) {
790 const ex& bb(b.op(0));
791 ex qbar = a;
792 int exp_b = ex_to<numeric>(b.op(1)).to_int();
793 for (int i=exp_b; i>0; i--) {
794 if (!divide_in_z(qbar, bb, q, var))
795 return false;
796 qbar = q;
797 }
798 return true;
799 }
800
801 if (is_exactly_a<mul>(b)) {
802 ex qbar = a;
803 for (const auto & it : b) {
804 sym_desc_vec sym_stats;
805 get_symbol_stats(a, it, sym_stats);
806 if (!divide_in_z(qbar, it, q, sym_stats.begin()))
807 return false;
808
809 qbar = q;
810 }
811 return true;
812 }
813
814 // Main symbol
815 const ex &x = var->sym;
816
817 // Compare degrees
818 int adeg = a.degree(x), bdeg = b.degree(x);
819 if (bdeg > adeg)
820 return false;
821
822#if USE_TRIAL_DIVISION
823
824 // Trial division with polynomial interpolation
825 int i, k;
826
827 // Compute values at evaluation points 0..adeg
828 vector<numeric> alpha; alpha.reserve(adeg + 1);
829 exvector u; u.reserve(adeg + 1);
830 numeric point = *_num0_p;
831 ex c;
832 for (i=0; i<=adeg; i++) {
833 ex bs = b.subs(x == point, subs_options::no_pattern);
834 while (bs.is_zero()) {
835 point += *_num1_p;
836 bs = b.subs(x == point, subs_options::no_pattern);
837 }
838 if (!divide_in_z(a.subs(x == point, subs_options::no_pattern), bs, c, var+1))
839 return false;
840 alpha.push_back(point);
841 u.push_back(c);
842 point += *_num1_p;
843 }
844
845 // Compute inverses
846 vector<numeric> rcp; rcp.reserve(adeg + 1);
847 rcp.push_back(*_num0_p);
848 for (k=1; k<=adeg; k++) {
849 numeric product = alpha[k] - alpha[0];
850 for (i=1; i<k; i++)
851 product *= alpha[k] - alpha[i];
852 rcp.push_back(product.inverse());
853 }
854
855 // Compute Newton coefficients
856 exvector v; v.reserve(adeg + 1);
857 v.push_back(u[0]);
858 for (k=1; k<=adeg; k++) {
859 ex temp = v[k - 1];
860 for (i=k-2; i>=0; i--)
861 temp = temp * (alpha[k] - alpha[i]) + v[i];
862 v.push_back((u[k] - temp) * rcp[k]);
863 }
864
865 // Convert from Newton form to standard form
866 c = v[adeg];
867 for (k=adeg-1; k>=0; k--)
868 c = c * (x - alpha[k]) + v[k];
869
870 if (c.degree(x) == (adeg - bdeg)) {
871 q = c.expand();
872 return true;
873 } else
874 return false;
875
876#else
877
878 // Polynomial long division (recursive)
879 ex r = a.expand();
880 if (r.is_zero())
881 return true;
882 int rdeg = adeg;
883 ex eb = b.expand();
884 ex blcoeff = eb.coeff(x, bdeg);
885 exvector v; v.reserve(std::max(rdeg - bdeg + 1, 0));
886 while (rdeg >= bdeg) {
887 ex term, rcoeff = r.coeff(x, rdeg);
888 if (!divide_in_z(rcoeff, blcoeff, term, var+1))
889 break;
890 term = (term * pow(x, rdeg - bdeg)).expand();
891 v.push_back(term);
892 r -= (term * eb).expand();
893 if (r.is_zero()) {
894 q = dynallocate<add>(v);
895#if USE_REMEMBER
896 dr_remember[ex2(a, b)] = exbool(q, true);
897#endif
898 return true;
899 }
900 rdeg = r.degree(x);
901 }
902#if USE_REMEMBER
903 dr_remember[ex2(a, b)] = exbool(q, false);
904#endif
905 return false;
906
907#endif
908}
909
910
911/*
912 * Separation of unit part, content part and primitive part of polynomials
913 */
914
922ex ex::unit(const ex &x) const
923{
924 ex c = expand().lcoeff(x);
925 if (is_exactly_a<numeric>(c))
927 else {
928 ex y;
929 if (get_first_symbol(c, y))
930 return c.unit(y);
931 else
932 throw(std::invalid_argument("invalid expression in unit()"));
933 }
934}
935
936
944ex ex::content(const ex &x) const
945{
946 if (is_exactly_a<numeric>(*this))
947 return info(info_flags::negative) ? -*this : *this;
948
949 ex e = expand();
950 if (e.is_zero())
951 return _ex0;
952
953 // First, divide out the integer content (which we can calculate very efficiently).
954 // If the leading coefficient of the quotient is an integer, we are done.
955 ex c = e.integer_content();
956 ex r = e / c;
957 int deg = r.degree(x);
958 ex lcoeff = r.coeff(x, deg);
960 return c;
961
962 // GCD of all coefficients
963 int ldeg = r.ldegree(x);
964 if (deg == ldeg)
965 return lcoeff * c / lcoeff.unit(x);
966 ex cont = _ex0;
967 for (int i=ldeg; i<=deg; i++)
968 cont = gcd(r.coeff(x, i), cont, nullptr, nullptr, false);
969 return cont * c;
970}
971
972
980ex ex::primpart(const ex &x) const
981{
982 // We need to compute the unit and content anyway, so call unitcontprim()
983 ex u, c, p;
984 unitcontprim(x, u, c, p);
985 return p;
986}
987
988
996ex ex::primpart(const ex &x, const ex &c) const
997{
998 if (is_zero() || c.is_zero())
999 return _ex0;
1000 if (is_exactly_a<numeric>(*this))
1001 return _ex1;
1002
1003 // Divide by unit and content to get primitive part
1004 ex u = unit(x);
1005 if (is_exactly_a<numeric>(c))
1006 return *this / (c * u);
1007 else
1008 return quo(*this, c * u, x, false);
1009}
1010
1011
1021void ex::unitcontprim(const ex &x, ex &u, ex &c, ex &p) const
1022{
1023 // Quick check for zero (avoid expanding)
1024 if (is_zero()) {
1025 u = _ex1;
1026 c = p = _ex0;
1027 return;
1028 }
1029
1030 // Special case: input is a number
1031 if (is_exactly_a<numeric>(*this)) {
1033 u = _ex_1;
1034 c = abs(ex_to<numeric>(*this));
1035 } else {
1036 u = _ex1;
1037 c = *this;
1038 }
1039 p = _ex1;
1040 return;
1041 }
1042
1043 // Expand input polynomial
1044 ex e = expand();
1045 if (e.is_zero()) {
1046 u = _ex1;
1047 c = p = _ex0;
1048 return;
1049 }
1050
1051 // Compute unit and content
1052 u = unit(x);
1053 c = content(x);
1054
1055 // Divide by unit and content to get primitive part
1056 if (c.is_zero()) {
1057 p = _ex0;
1058 return;
1059 }
1060 if (is_exactly_a<numeric>(c))
1061 p = *this / (c * u);
1062 else
1063 p = quo(e, c * u, x, false);
1064}
1065
1066
1067/*
1068 * GCD of multivariate polynomials
1069 */
1070
1080static ex sr_gcd(const ex &a, const ex &b, sym_desc_vec::const_iterator var)
1081{
1082#if STATISTICS
1083 sr_gcd_called++;
1084#endif
1085
1086 // The first symbol is our main variable
1087 const ex &x = var->sym;
1088
1089 // Sort c and d so that c has higher degree
1090 ex c, d;
1091 int adeg = a.degree(x), bdeg = b.degree(x);
1092 int cdeg, ddeg;
1093 if (adeg >= bdeg) {
1094 c = a;
1095 d = b;
1096 cdeg = adeg;
1097 ddeg = bdeg;
1098 } else {
1099 c = b;
1100 d = a;
1101 cdeg = bdeg;
1102 ddeg = adeg;
1103 }
1104
1105 // Remove content from c and d, to be attached to GCD later
1106 ex cont_c = c.content(x);
1107 ex cont_d = d.content(x);
1108 ex gamma = gcd(cont_c, cont_d, nullptr, nullptr, false);
1109 if (ddeg == 0)
1110 return gamma;
1111 c = c.primpart(x, cont_c);
1112 d = d.primpart(x, cont_d);
1113
1114 // First element of subresultant sequence
1115 ex r = _ex0, ri = _ex1, psi = _ex1;
1116 int delta = cdeg - ddeg;
1117
1118 for (;;) {
1119
1120 // Calculate polynomial pseudo-remainder
1121 r = prem(c, d, x, false);
1122 if (r.is_zero())
1123 return gamma * d.primpart(x);
1124
1125 c = d;
1126 cdeg = ddeg;
1127 if (!divide_in_z(r, ri * pow(psi, delta), d, var))
1128 throw(std::runtime_error("invalid expression in sr_gcd(), division failed"));
1129 ddeg = d.degree(x);
1130 if (ddeg == 0) {
1131 if (is_exactly_a<numeric>(r))
1132 return gamma;
1133 else
1134 return gamma * r.primpart(x);
1135 }
1136
1137 // Next element of subresultant sequence
1138 ri = c.expand().lcoeff(x);
1139 if (delta == 1)
1140 psi = ri;
1141 else if (delta)
1142 divide_in_z(pow(ri, delta), pow(psi, delta-1), psi, var+1);
1143 delta = cdeg - ddeg;
1144 }
1145}
1146
1147
1154{
1155 return bp->max_coefficient();
1156}
1157
1161{
1162 return *_num1_p;
1163}
1164
1166{
1167 return abs(*this);
1168}
1169
1171{
1172 GINAC_ASSERT(is_exactly_a<numeric>(overall_coeff));
1173 numeric cur_max = abs(ex_to<numeric>(overall_coeff));
1174 for (auto & it : seq) {
1175 numeric a;
1176 GINAC_ASSERT(!is_exactly_a<numeric>(it.rest));
1177 a = abs(ex_to<numeric>(it.coeff));
1178 if (a > cur_max)
1179 cur_max = a;
1180 }
1181 return cur_max;
1182}
1183
1185{
1186#ifdef DO_GINAC_ASSERT
1187 for (auto & it : seq) {
1188 GINAC_ASSERT(!is_exactly_a<numeric>(recombine_pair_to_ex(it)));
1189 }
1190#endif // def DO_GINAC_ASSERT
1191 GINAC_ASSERT(is_exactly_a<numeric>(overall_coeff));
1192 return abs(ex_to<numeric>(overall_coeff));
1193}
1194
1195
1202ex basic::smod(const numeric &xi) const
1203{
1204 return *this;
1205}
1206
1207ex numeric::smod(const numeric &xi) const
1208{
1209 return GiNaC::smod(*this, xi);
1210}
1211
1212ex add::smod(const numeric &xi) const
1213{
1214 epvector newseq;
1215 newseq.reserve(seq.size()+1);
1216 for (auto & it : seq) {
1217 GINAC_ASSERT(!is_exactly_a<numeric>(it.rest));
1218 numeric coeff = GiNaC::smod(ex_to<numeric>(it.coeff), xi);
1219 if (!coeff.is_zero())
1220 newseq.push_back(expair(it.rest, coeff));
1221 }
1222 GINAC_ASSERT(is_exactly_a<numeric>(overall_coeff));
1223 numeric coeff = GiNaC::smod(ex_to<numeric>(overall_coeff), xi);
1224 return dynallocate<add>(std::move(newseq), coeff);
1225}
1226
1227ex mul::smod(const numeric &xi) const
1228{
1229#ifdef DO_GINAC_ASSERT
1230 for (auto & it : seq) {
1231 GINAC_ASSERT(!is_exactly_a<numeric>(recombine_pair_to_ex(it)));
1232 }
1233#endif // def DO_GINAC_ASSERT
1234 mul & mulcopy = dynallocate<mul>(*this);
1235 GINAC_ASSERT(is_exactly_a<numeric>(overall_coeff));
1236 mulcopy.overall_coeff = GiNaC::smod(ex_to<numeric>(overall_coeff),xi);
1239 return mulcopy;
1240}
1241
1242
1244static ex interpolate(const ex &gamma, const numeric &xi, const ex &x, int degree_hint = 1)
1245{
1246 exvector g; g.reserve(degree_hint);
1247 ex e = gamma;
1248 numeric rxi = xi.inverse();
1249 for (int i=0; !e.is_zero(); i++) {
1250 ex gi = e.smod(xi);
1251 g.push_back(gi * pow(x, i));
1252 e = (e - gi) * rxi;
1253 }
1254 return dynallocate<add>(g);
1255}
1256
1259
1276static bool heur_gcd_z(ex& res, const ex &a, const ex &b, ex *ca, ex *cb,
1277 sym_desc_vec::const_iterator var)
1278{
1279#if STATISTICS
1280 heur_gcd_called++;
1281#endif
1282
1283 // Algorithm only works for non-vanishing input polynomials
1284 if (a.is_zero() || b.is_zero())
1285 return false;
1286
1287 // GCD of two numeric values -> CLN
1288 if (is_exactly_a<numeric>(a) && is_exactly_a<numeric>(b)) {
1289 numeric g = gcd(ex_to<numeric>(a), ex_to<numeric>(b));
1290 if (ca)
1291 *ca = ex_to<numeric>(a) / g;
1292 if (cb)
1293 *cb = ex_to<numeric>(b) / g;
1294 res = g;
1295 return true;
1296 }
1297
1298 // The first symbol is our main variable
1299 const ex &x = var->sym;
1300
1301 // Remove integer content
1303 numeric rgc = gc.inverse();
1304 ex p = a * rgc;
1305 ex q = b * rgc;
1306 int maxdeg = std::max(p.degree(x), q.degree(x));
1307
1308 // Find evaluation point
1309 numeric mp = p.max_coefficient();
1310 numeric mq = q.max_coefficient();
1311 numeric xi;
1312 if (mp > mq)
1313 xi = mq * (*_num2_p) + (*_num2_p);
1314 else
1315 xi = mp * (*_num2_p) + (*_num2_p);
1316
1317 // 6 tries maximum
1318 for (int t=0; t<6; t++) {
1319 if (xi.int_length() * maxdeg > 100000) {
1320 throw gcdheu_failed();
1321 }
1322
1323 // Apply evaluation homomorphism and calculate GCD
1324 ex cp, cq;
1325 ex gamma;
1326 bool found = heur_gcd_z(gamma,
1329 &cp, &cq, var+1);
1330 if (found) {
1331 gamma = gamma.expand();
1332 // Reconstruct polynomial from GCD of mapped polynomials
1333 ex g = interpolate(gamma, xi, x, maxdeg);
1334
1335 // Remove integer content
1336 g /= g.integer_content();
1337
1338 // If the calculated polynomial divides both p and q, this is the GCD
1339 ex dummy;
1340 if (divide_in_z(p, g, ca ? *ca : dummy, var) && divide_in_z(q, g, cb ? *cb : dummy, var)) {
1341 g *= gc;
1342 res = g;
1343 return true;
1344 }
1345 }
1346
1347 // Next evaluation point
1348 xi = iquo(xi * isqrt(isqrt(xi)) * numeric(73794), numeric(27011));
1349 }
1350 return false;
1351}
1352
1370static bool heur_gcd(ex& res, const ex& a, const ex& b, ex *ca, ex *cb,
1371 sym_desc_vec::const_iterator var)
1372{
1375 try {
1376 return heur_gcd_z(res, a, b, ca, cb, var);
1377 } catch (gcdheu_failed) {
1378 return false;
1379 }
1380 }
1381
1382 // convert polynomials to Z[X]
1384 const numeric ab_lcm = lcmcoeff(b, a_lcm);
1385
1386 const ex ai = a*ab_lcm;
1387 const ex bi = b*ab_lcm;
1389 throw std::logic_error("heur_gcd: not an integer polynomial [1]");
1390
1392 throw std::logic_error("heur_gcd: not an integer polynomial [2]");
1393
1394 bool found = false;
1395 try {
1396 found = heur_gcd_z(res, ai, bi, ca, cb, var);
1397 } catch (gcdheu_failed) {
1398 return false;
1399 }
1400
1401 // GCD is not unique, it's defined up to a unit (i.e. invertible
1402 // element). If the coefficient ring is a field, every its element is
1403 // invertible, so one can multiply the polynomial GCD with any element
1404 // of the coefficient field. We use this ambiguity to make cofactors
1405 // integer polynomials.
1406 if (found)
1407 res /= ab_lcm;
1408 return found;
1409}
1410
1411
1412// gcd helper to handle partially factored polynomials (to avoid expanding
1413// large expressions). At least one of the arguments should be a power.
1414static ex gcd_pf_pow(const ex& a, const ex& b, ex* ca, ex* cb);
1415
1416// gcd helper to handle partially factored polynomials (to avoid expanding
1417// large expressions). At least one of the arguments should be a product.
1418static ex gcd_pf_mul(const ex& a, const ex& b, ex* ca, ex* cb);
1419
1432ex gcd(const ex &a, const ex &b, ex *ca, ex *cb, bool check_args, unsigned options)
1433{
1434#if STATISTICS
1435 gcd_called++;
1436#endif
1437
1438 // GCD of numerics -> CLN
1439 if (is_exactly_a<numeric>(a) && is_exactly_a<numeric>(b)) {
1440 numeric g = gcd(ex_to<numeric>(a), ex_to<numeric>(b));
1441 if (ca || cb) {
1442 if (g.is_zero()) {
1443 if (ca)
1444 *ca = _ex0;
1445 if (cb)
1446 *cb = _ex0;
1447 } else {
1448 if (ca)
1449 *ca = ex_to<numeric>(a) / g;
1450 if (cb)
1451 *cb = ex_to<numeric>(b) / g;
1452 }
1453 }
1454 return g;
1455 }
1456
1457 // Check arguments
1459 throw(std::invalid_argument("gcd: arguments must be polynomials over the rationals"));
1460 }
1461
1462 // Partially factored cases (to avoid expanding large expressions)
1464 if (is_exactly_a<mul>(a) || is_exactly_a<mul>(b))
1465 return gcd_pf_mul(a, b, ca, cb);
1466#if FAST_COMPARE
1467 if (is_exactly_a<power>(a) || is_exactly_a<power>(b))
1468 return gcd_pf_pow(a, b, ca, cb);
1469#endif
1470 }
1471
1472 // Some trivial cases
1473 ex aex = a.expand();
1474 if (aex.is_zero()) {
1475 if (ca)
1476 *ca = _ex0;
1477 if (cb)
1478 *cb = _ex1;
1479 return b;
1480 }
1481 ex bex = b.expand();
1482 if (bex.is_zero()) {
1483 if (ca)
1484 *ca = _ex1;
1485 if (cb)
1486 *cb = _ex0;
1487 return a;
1488 }
1489 if (aex.is_equal(_ex1) || bex.is_equal(_ex1)) {
1490 if (ca)
1491 *ca = a;
1492 if (cb)
1493 *cb = b;
1494 return _ex1;
1495 }
1496#if FAST_COMPARE
1497 if (a.is_equal(b)) {
1498 if (ca)
1499 *ca = _ex1;
1500 if (cb)
1501 *cb = _ex1;
1502 return a;
1503 }
1504#endif
1505
1506 if (is_a<symbol>(aex)) {
1507 if (! bex.subs(aex==_ex0, subs_options::no_pattern).is_zero()) {
1508 if (ca)
1509 *ca = a;
1510 if (cb)
1511 *cb = b;
1512 return _ex1;
1513 }
1514 }
1515
1516 if (is_a<symbol>(bex)) {
1517 if (! aex.subs(bex==_ex0, subs_options::no_pattern).is_zero()) {
1518 if (ca)
1519 *ca = a;
1520 if (cb)
1521 *cb = b;
1522 return _ex1;
1523 }
1524 }
1525
1526 if (is_exactly_a<numeric>(aex)) {
1527 numeric bcont = bex.integer_content();
1528 numeric g = gcd(ex_to<numeric>(aex), bcont);
1529 if (ca)
1530 *ca = ex_to<numeric>(aex)/g;
1531 if (cb)
1532 *cb = bex/g;
1533 return g;
1534 }
1535
1536 if (is_exactly_a<numeric>(bex)) {
1537 numeric acont = aex.integer_content();
1538 numeric g = gcd(ex_to<numeric>(bex), acont);
1539 if (ca)
1540 *ca = aex/g;
1541 if (cb)
1542 *cb = ex_to<numeric>(bex)/g;
1543 return g;
1544 }
1545
1546 // Gather symbol statistics
1547 sym_desc_vec sym_stats;
1548 get_symbol_stats(a, b, sym_stats);
1549
1550 // The symbol with least degree which is contained in both polynomials
1551 // is our main variable
1552 auto vari = sym_stats.begin();
1553 while ((vari != sym_stats.end()) &&
1554 (((vari->ldeg_b == 0) && (vari->deg_b == 0)) ||
1555 ((vari->ldeg_a == 0) && (vari->deg_a == 0))))
1556 vari++;
1557
1558 // No common symbols at all, just return 1:
1559 if (vari == sym_stats.end()) {
1560 // N.B: keep cofactors factored
1561 if (ca)
1562 *ca = a;
1563 if (cb)
1564 *cb = b;
1565 return _ex1;
1566 }
1567 // move symbol contained only in one of the polynomials to the end:
1568 rotate(sym_stats.begin(), vari, sym_stats.end());
1569
1570 sym_desc_vec::const_iterator var = sym_stats.begin();
1571 const ex &x = var->sym;
1572
1573 // Cancel trivial common factor
1574 int ldeg_a = var->ldeg_a;
1575 int ldeg_b = var->ldeg_b;
1576 int min_ldeg = std::min(ldeg_a,ldeg_b);
1577 if (min_ldeg > 0) {
1578 ex common = pow(x, min_ldeg);
1579 return gcd((aex / common).expand(), (bex / common).expand(), ca, cb, false) * common;
1580 }
1581
1582 // Try to eliminate variables
1583 if (var->deg_a == 0 && var->deg_b != 0 ) {
1584 ex bex_u, bex_c, bex_p;
1585 bex.unitcontprim(x, bex_u, bex_c, bex_p);
1586 ex g = gcd(aex, bex_c, ca, cb, false);
1587 if (cb)
1588 *cb *= bex_u * bex_p;
1589 return g;
1590 } else if (var->deg_b == 0 && var->deg_a != 0) {
1591 ex aex_u, aex_c, aex_p;
1592 aex.unitcontprim(x, aex_u, aex_c, aex_p);
1593 ex g = gcd(aex_c, bex, ca, cb, false);
1594 if (ca)
1595 *ca *= aex_u * aex_p;
1596 return g;
1597 }
1598
1599 // Try heuristic algorithm first, fall back to PRS if that failed
1600 ex g;
1602 bool found = heur_gcd(g, aex, bex, ca, cb, var);
1603 if (found) {
1604 // heur_gcd have already computed cofactors...
1605 if (g.is_equal(_ex1)) {
1606 // ... but we want to keep them factored if possible.
1607 if (ca)
1608 *ca = a;
1609 if (cb)
1610 *cb = b;
1611 }
1612 return g;
1613 }
1614#if STATISTICS
1615 else {
1616 heur_gcd_failed++;
1617 }
1618#endif
1619 }
1621 g = sr_gcd(aex, bex, var);
1622 } else {
1623 exvector vars;
1624 for (std::size_t n = sym_stats.size(); n-- != 0; )
1625 vars.push_back(sym_stats[n].sym);
1626 g = chinrem_gcd(aex, bex, vars);
1627 }
1628
1629 if (g.is_equal(_ex1)) {
1630 // Keep cofactors factored if possible
1631 if (ca)
1632 *ca = a;
1633 if (cb)
1634 *cb = b;
1635 } else {
1636 if (ca)
1637 divide(aex, g, *ca, false);
1638 if (cb)
1639 divide(bex, g, *cb, false);
1640 }
1641 return g;
1642}
1643
1644// gcd helper to handle partially factored polynomials (to avoid expanding
1645// large expressions). Both arguments should be powers.
1646static ex gcd_pf_pow_pow(const ex& a, const ex& b, ex* ca, ex* cb)
1647{
1648 ex p = a.op(0);
1649 const ex& exp_a = a.op(1);
1650 ex pb = b.op(0);
1651 const ex& exp_b = b.op(1);
1652
1653 // a = p^n, b = p^m, gcd = p^min(n, m)
1654 if (p.is_equal(pb)) {
1655 if (exp_a < exp_b) {
1656 if (ca)
1657 *ca = _ex1;
1658 if (cb)
1659 *cb = pow(p, exp_b - exp_a);
1660 return pow(p, exp_a);
1661 } else {
1662 if (ca)
1663 *ca = pow(p, exp_a - exp_b);
1664 if (cb)
1665 *cb = _ex1;
1666 return pow(p, exp_b);
1667 }
1668 }
1669
1670 ex p_co, pb_co;
1671 ex p_gcd = gcd(p, pb, &p_co, &pb_co, false);
1672 // a(x) = p(x)^n, b(x) = p_b(x)^m, gcd (p, p_b) = 1 ==> gcd(a,b) = 1
1673 if (p_gcd.is_equal(_ex1)) {
1674 if (ca)
1675 *ca = a;
1676 if (cb)
1677 *cb = b;
1678 return _ex1;
1679 }
1680
1681 // there are common factors:
1682 // a(x) = g(x)^n A(x)^n, b(x) = g(x)^m B(x)^m ==>
1683 // gcd(a, b) = g(x)^n gcd(A(x)^n, g(x)^(n-m) B(x)^m
1684 if (exp_a < exp_b) {
1685 ex pg = gcd(pow(p_co, exp_a), pow(p_gcd, exp_b-exp_a)*pow(pb_co, exp_b), ca, cb, false);
1686 return pow(p_gcd, exp_a)*pg;
1687 } else {
1688 ex pg = gcd(pow(p_gcd, exp_a - exp_b)*pow(p_co, exp_a), pow(pb_co, exp_b), ca, cb, false);
1689 return pow(p_gcd, exp_b)*pg;
1690 }
1691}
1692
1693static ex gcd_pf_pow(const ex& a, const ex& b, ex* ca, ex* cb)
1694{
1695 if (is_exactly_a<power>(a) && is_exactly_a<power>(b))
1696 return gcd_pf_pow_pow(a, b, ca, cb);
1697
1698 if (is_exactly_a<power>(b) && (! is_exactly_a<power>(a)))
1699 return gcd_pf_pow(b, a, cb, ca);
1700
1701 GINAC_ASSERT(is_exactly_a<power>(a));
1702
1703 ex p = a.op(0);
1704 const ex& exp_a = a.op(1);
1705 if (p.is_equal(b)) {
1706 // a = p^n, b = p, gcd = p
1707 if (ca)
1708 *ca = pow(p, exp_a - 1);
1709 if (cb)
1710 *cb = _ex1;
1711 return p;
1712 }
1713 if (is_a<symbol>(p)) {
1714 // Cancel trivial common factor
1715 int ldeg_a = ex_to<numeric>(exp_a).to_int();
1716 int ldeg_b = b.ldegree(p);
1717 int min_ldeg = std::min(ldeg_a, ldeg_b);
1718 if (min_ldeg > 0) {
1719 ex common = pow(p, min_ldeg);
1720 return gcd(pow(p, ldeg_a - min_ldeg), (b / common).expand(), ca, cb, false) * common;
1721 }
1722 }
1723
1724 ex p_co, bpart_co;
1725 ex p_gcd = gcd(p, b, &p_co, &bpart_co, false);
1726
1727 if (p_gcd.is_equal(_ex1)) {
1728 // a(x) = p(x)^n, gcd(p, b) = 1 ==> gcd(a, b) = 1
1729 if (ca)
1730 *ca = a;
1731 if (cb)
1732 *cb = b;
1733 return _ex1;
1734 }
1735 // a(x) = g(x)^n A(x)^n, b(x) = g(x) B(x) ==> gcd(a, b) = g(x) gcd(g(x)^(n-1) A(x)^n, B(x))
1736 ex rg = gcd(pow(p_gcd, exp_a-1)*pow(p_co, exp_a), bpart_co, ca, cb, false);
1737 return p_gcd*rg;
1738}
1739
1740static ex gcd_pf_mul(const ex& a, const ex& b, ex* ca, ex* cb)
1741{
1742 if (is_exactly_a<mul>(a) && is_exactly_a<mul>(b)
1743 && (b.nops() > a.nops()))
1744 return gcd_pf_mul(b, a, cb, ca);
1745
1746 if (is_exactly_a<mul>(b) && (!is_exactly_a<mul>(a)))
1747 return gcd_pf_mul(b, a, cb, ca);
1748
1749 GINAC_ASSERT(is_exactly_a<mul>(a));
1750 size_t num = a.nops();
1751 exvector g; g.reserve(num);
1752 exvector acc_ca; acc_ca.reserve(num);
1753 ex part_b = b;
1754 for (size_t i=0; i<num; i++) {
1755 ex part_ca, part_cb;
1756 g.push_back(gcd(a.op(i), part_b, &part_ca, &part_cb, false));
1757 acc_ca.push_back(part_ca);
1758 part_b = part_cb;
1759 }
1760 if (ca)
1761 *ca = dynallocate<mul>(acc_ca);
1762 if (cb)
1763 *cb = part_b;
1764 return dynallocate<mul>(g);
1765}
1766
1774ex lcm(const ex &a, const ex &b, bool check_args)
1775{
1776 if (is_exactly_a<numeric>(a) && is_exactly_a<numeric>(b))
1777 return lcm(ex_to<numeric>(a), ex_to<numeric>(b));
1779 throw(std::invalid_argument("lcm: arguments must be polynomials over the rationals"));
1780
1781 ex ca, cb;
1782 ex g = gcd(a, b, &ca, &cb, false);
1783 return ca * cb * g;
1784}
1785
1786
1787/*
1788 * Square-free factorization
1789 */
1790
1798static epvector sqrfree_yun(const ex &a, const symbol &x)
1799{
1800 ex w = a;
1801 ex z = w.diff(x);
1802 ex g = gcd(w, z);
1803 if (g.is_zero()) {
1804 // manifest zero or hidden zero
1805 return {};
1806 }
1807 if (g.is_equal(_ex1)) {
1808 // w(x) and w'(x) share no factors: w(x) is square-free
1809 return {expair(a, _ex1)};
1810 }
1811
1813 ex i = 0; // exponent
1814 do {
1815 w = quo(w, g, x);
1816 if (w.is_zero()) {
1817 // hidden zero
1818 break;
1819 }
1820 z = quo(z, g, x) - w.diff(x);
1821 i += 1;
1822 if (w.is_equal(x)) {
1823 // shortcut for x^n with n ∈ ℕ
1824 i += quo(z, w.diff(x), x);
1825 factors.push_back(expair(w, i));
1826 break;
1827 }
1828 g = gcd(w, z);
1829 if (!g.is_equal(_ex1)) {
1830 factors.push_back(expair(g, i));
1831 }
1832 } while (!z.is_zero());
1833
1834 // correct for lost factor
1835 // (being based on GCDs, Yun's algorithm only finds factors up to a unit)
1836 const ex lost_factor = quo(a, mul{factors}, x);
1837 if (lost_factor.is_equal(_ex1)) {
1838 // trivial lost factor
1839 return factors;
1840 }
1841 if (!factors.empty() && factors[0].coeff.is_equal(1)) {
1842 // multiply factor^1 with lost_factor
1843 factors[0].rest *= lost_factor;
1844 return factors;
1845 }
1846 // no factor^1: prepend lost_factor^1 to the results
1847 epvector results = {expair(lost_factor, 1)};
1848 std::move(factors.begin(), factors.end(), std::back_inserter(results));
1849 return results;
1850}
1851
1852
1888ex sqrfree(const ex &a, const lst &l)
1889{
1890 if (is_exactly_a<numeric>(a) ||
1891 is_a<symbol>(a)) // shortcuts
1892 return a;
1893
1894 // If no lst of variables to factorize in was specified we have to
1895 // invent one now. Maybe one can optimize here by reversing the order
1896 // or so, I don't know.
1897 lst args;
1898 if (l.nops()==0) {
1899 sym_desc_vec sdv;
1900 get_symbol_stats(a, _ex0, sdv);
1901 for (auto & it : sdv)
1902 args.append(it.sym);
1903 } else {
1904 args = l;
1905 }
1906
1907 // Find the symbol to factor in at this stage
1908 if (!is_a<symbol>(args.op(0)))
1909 throw (std::runtime_error("sqrfree(): invalid factorization variable"));
1910 const symbol &x = ex_to<symbol>(args.op(0));
1911
1912 // convert the argument from something in Q[X] to something in Z[X]
1914 const ex tmp = multiply_lcm(a, lcm);
1915
1916 // find the factors
1917 epvector factors = sqrfree_yun(tmp, x);
1918 if (factors.empty()) {
1919 // the polynomial was a hidden zero
1920 return _ex0;
1921 }
1922
1923 // remove symbol x and proceed recursively with the remaining symbols
1924 args.remove_first();
1925
1926 // recurse down the factors in remaining variables
1927 if (args.nops()>0) {
1928 for (auto & it : factors)
1929 it.rest = sqrfree(it.rest, args);
1930 }
1931
1932 // Done with recursion, now construct the final result
1933 ex result = mul(factors);
1934
1935 // Put in the rational overall factor again and return
1936 return result * lcm.inverse();
1937}
1938
1939
1947ex sqrfree_parfrac(const ex & a, const symbol & x)
1948{
1949 // Find numerator and denominator
1950 ex nd = numer_denom(a);
1951 ex numer = nd.op(0), denom = nd.op(1);
1952//std::clog << "numer = " << numer << ", denom = " << denom << std::endl;
1953
1954 // Convert N(x)/D(x) -> Q(x) + R(x)/D(x), so degree(R) < degree(D)
1955 ex red_poly = quo(numer, denom, x), red_numer = rem(numer, denom, x).expand();
1956//std::clog << "red_poly = " << red_poly << ", red_numer = " << red_numer << std::endl;
1957
1958 // Factorize denominator and compute cofactors
1959 epvector yun = sqrfree_yun(denom, x);
1960 exvector factor, cofac;
1961 size_t dim = 0;
1962 for (size_t i=0; i<yun.size(); i++) {
1963 numeric i_exponent = ex_to<numeric>(yun[i].coeff);
1964 for (size_t j=0; j<i_exponent; j++) {
1965 factor.push_back(pow(yun[i].rest, j+1));
1966 dim += degree(yun[i].rest, x);
1967 ex prod = _ex1;
1968 for (size_t k=0; k<yun.size(); k++) {
1969 if (yun[k].coeff == i_exponent)
1970 prod *= pow(yun[k].rest, i_exponent-1-j);
1971 else
1972 prod *= pow(yun[k].rest, yun[k].coeff);
1973 }
1974 cofac.push_back(prod.expand());
1975 }
1976 }
1977//std::clog << "factors : " << exprseq(factor) << std::endl;
1978//std::clog << "cofactors: " << exprseq(cofac) << std::endl;
1979
1980 // Construct linear system for decomposition
1981 matrix sys(dim, dim);
1982 matrix rhs(dim, 1);
1983 matrix vars(dim, 1);
1984 for (size_t i=0, n=0, f=0; i<yun.size(); i++) {
1985 size_t i_expo = to_int(ex_to<numeric>(yun[i].coeff));
1986 for (size_t j=0; j<i_expo; j++) {
1987 for (size_t k=0; k<size_t(degree(yun[i].rest, x)); k++) {
1988 GINAC_ASSERT(n < dim && f < factor.size());
1989
1990 // column n of coefficient matrix
1991 for (size_t r=0; r+k<dim; r++) {
1992 sys(r+k, n) = cofac[f].coeff(x, r);
1993 }
1994
1995 // element n of right hand side vector
1996 rhs(n, 0) = red_numer.coeff(x, n);
1997
1998 // element n of free variables vector
1999 vars(n, 0) = symbol();
2000
2001 n++;
2002 }
2003 f++;
2004 }
2005 }
2006//std::clog << "coeffs: " << sys << std::endl;
2007//std::clog << "rhs : " << rhs << std::endl;
2008
2009 // Solve resulting linear system and sum up decomposed fractions
2010 matrix sol = sys.solve(vars, rhs);
2011//std::clog << "sol : " << sol << std::endl;
2012 ex sum = red_poly;
2013 for (size_t i=0, n=0, f=0; i<yun.size(); i++) {
2014 size_t i_expo = to_int(ex_to<numeric>(yun[i].coeff));
2015 for (size_t j=0; j<i_expo; j++) {
2016 ex frac_numer = 0;
2017 for (size_t k=0; k<size_t(degree(yun[i].rest, x)); k++) {
2018 GINAC_ASSERT(n < dim && f < factor.size());
2019 frac_numer += sol(n, 0) * pow(x, k);
2020 n++;
2021 }
2022 sum += frac_numer / factor[f];
2023
2024 f++;
2025 }
2026 }
2027
2028 return sum;
2029}
2030
2031
2032/*
2033 * Normal form of rational functions
2034 */
2035
2036/*
2037 * Note: The internal normal() functions (= basic::normal() and overloaded
2038 * functions) all return lists of the form {numerator, denominator}. This
2039 * is to get around mul::eval()'s automatic expansion of numeric coefficients.
2040 * E.g. (a+b)/3 is automatically converted to a/3+b/3 but we want to keep
2041 * the information that (a+b) is the numerator and 3 is the denominator.
2042 */
2043
2044
2077static ex replace_with_symbol(const ex & e, exmap & repl, exmap & rev_lookup, lst & modifier)
2078{
2079 // Since the repl contains replaced expressions we should search for them
2080 ex e_replaced = e.subs(repl, subs_options::no_pattern);
2081
2082 // Expression already replaced? Then return the assigned symbol
2083 auto it = rev_lookup.find(e_replaced);
2084 if (it != rev_lookup.end())
2085 return it->second;
2086
2087 // The expression can be the base of substituted power, which requires a more careful search
2088 if (! is_a<numeric>(e_replaced))
2089 for (auto & it : repl)
2090 if (is_a<power>(it.second) && e_replaced.is_equal(it.second.op(0))) {
2091 ex degree = pow(it.second.op(1), _ex_1);
2092 if (is_a<numeric>(degree) && ex_to<numeric>(degree).is_integer())
2093 return pow(it.first, degree);
2094 }
2095
2096 // We treat powers and the exponent functions differently because
2097 // they can be rationalised more efficiently
2098 if (is_a<function>(e_replaced) && is_ex_the_function(e_replaced, exp)) {
2099 for (auto & it : repl) {
2100 if (is_a<function>(it.second) && is_ex_the_function(it.second, exp)) {
2101 ex ratio = normal(e_replaced.op(0) / it.second.op(0));
2102 if (is_a<numeric>(ratio) && ex_to<numeric>(ratio).is_rational()) {
2103 // Different exponents can be treated as powers of the same basic equation
2104 if (ex_to<numeric>(ratio).is_integer()) {
2105 // If ratio is an integer then this is simply the power of the existing symbol.
2106 // std::clog << e_replaced << " is a " << ratio << " power of " << it.first << std::endl;
2107 return dynallocate<power>(it.first, ratio);
2108 } else {
2109 // otherwise we need to give the replacement pattern to change
2110 // the previous expression...
2111 ex es = dynallocate<symbol>();
2112 ex Num = numer(ratio);
2113 modifier.append(it.first == power(es, denom(ratio)));
2114 // std::clog << e_replaced << " is power " << Num << " and "
2115 // << it.first << " is power " << denom(ratio) << " of the common base "
2116 // << exp(e_replaced.op(0)/Num) << std::endl;
2117 // ... and modify the replacement tables
2118 rev_lookup.erase(it.second);
2119 rev_lookup.insert({exp(e_replaced.op(0)/Num), es});
2120 repl.erase(it.first);
2121 repl.insert({es, exp(e_replaced.op(0)/Num)});
2122 return dynallocate<power>(es, Num);
2123 }
2124 }
2125 }
2126 }
2127 } else if (is_a<power>(e_replaced) && !is_a<numeric>(e_replaced.op(0)) // We do not replace simple monomials like x^3 or sqrt(2)
2128 && ! (is_a<symbol>(e_replaced.op(0))
2129 && is_a<numeric>(e_replaced.op(1)) && ex_to<numeric>(e_replaced.op(1)).is_integer())) {
2130 for (auto & it : repl) {
2131 if (e_replaced.op(0).is_equal(it.second) // The base is an allocated symbol or base of power
2132 || (is_a<power>(it.second) && e_replaced.op(0).is_equal(it.second.op(0)))) {
2133 ex ratio; // We bind together two above cases
2134 if (is_a<power>(it.second))
2135 ratio = normal(e_replaced.op(1) / it.second.op(1));
2136 else
2137 ratio = e_replaced.op(1);
2138 if (is_a<numeric>(ratio) && ex_to<numeric>(ratio).is_rational()) {
2139 // Different powers can be treated as powers of the same basic equation
2140 if (ex_to<numeric>(ratio).is_integer()) {
2141 // If ratio is an integer then this is simply the power of the existing symbol.
2142 //std::clog << e_replaced << " is a " << ratio << " power of " << it.first << std::endl;
2143 return dynallocate<power>(it.first, ratio);
2144 } else {
2145 // otherwise we need to give the replacement pattern to change
2146 // the previous expression...
2147 ex es = dynallocate<symbol>();
2148 ex Num = numer(ratio);
2149 modifier.append(it.first == power(es, denom(ratio)));
2150 //std::clog << e_replaced << " is power " << Num << " and "
2151 // << it.first << " is power " << denom(ratio) << " of the common base "
2152 // << pow(e_replaced.op(0), e_replaced.op(1)/Num) << std::endl;
2153 // ... and modify the replacement tables
2154 rev_lookup.erase(it.second);
2155 rev_lookup.insert({pow(e_replaced.op(0), e_replaced.op(1)/Num), es});
2156 repl.erase(it.first);
2157 repl.insert({es, pow(e_replaced.op(0), e_replaced.op(1)/Num)});
2158 return dynallocate<power>(es, Num);
2159 }
2160 }
2161 }
2162 }
2163 // There is no existing substitution, thus we are creating a new one.
2164 // This needs to be done separately to treat possible occurrences of
2165 // b = e_replaced.op(0) elsewhere in the expression as pow(b, 1).
2166 ex degree = pow(e_replaced.op(1), _ex_1);
2167 if (is_a<numeric>(degree) && ex_to<numeric>(degree).is_integer()) {
2168 ex es = dynallocate<symbol>();
2169 modifier.append(e_replaced.op(0) == power(es, degree));
2170 repl.insert({es, e_replaced});
2171 rev_lookup.insert({e_replaced, es});
2172 return es;
2173 }
2174 }
2175
2176 // Otherwise create new symbol and add to list, taking care that the
2177 // replacement expression doesn't itself contain symbols from repl,
2178 // because subs() is not recursive
2179 ex es = dynallocate<symbol>();
2180 repl.insert(std::make_pair(es, e_replaced));
2181 rev_lookup.insert(std::make_pair(e_replaced, es));
2182 return es;
2183}
2184
2190static ex replace_with_symbol(const ex & e, exmap & repl)
2191{
2192 // Since the repl contains replaced expressions we should search for them
2193 ex e_replaced = e.subs(repl, subs_options::no_pattern);
2194
2195 // Expression already replaced? Then return the assigned symbol
2196 for (auto & it : repl)
2197 if (it.second.is_equal(e_replaced))
2198 return it.first;
2199
2200 // Otherwise create new symbol and add to list, taking care that the
2201 // replacement expression doesn't itself contain symbols from repl,
2202 // because subs() is not recursive
2203 ex es = dynallocate<symbol>();
2204 repl.insert(std::make_pair(es, e_replaced));
2205 return es;
2206}
2207
2208
2211 ex operator()(const ex & e) override { return normal(e); }
2212};
2213
2217ex basic::normal(exmap & repl, exmap & rev_lookup, lst & modifier) const
2218{
2219 if (nops() == 0)
2220 return dynallocate<lst>({replace_with_symbol(*this, repl, rev_lookup, modifier), _ex1});
2221
2222 normal_map_function map_normal;
2223 size_t nmod = modifier.nops(); // To watch new modifiers to the replacement list
2224 ex result = replace_with_symbol(map(map_normal), repl, rev_lookup, modifier);
2225 for (size_t imod = nmod; imod < modifier.nops(); ++imod) {
2226 exmap this_repl;
2227 this_repl.insert(std::make_pair(modifier.op(imod).op(0), modifier.op(imod).op(1)));
2228 result = result.subs(this_repl, subs_options::no_pattern);
2229 }
2230
2231 // Sometimes we may obtain negative powers, they need to be placed to denominator
2232 if (is_a<power>(result) && result.op(1).info(info_flags::negative))
2233 return dynallocate<lst>({_ex1, power(result.op(0), -result.op(1))});
2234 else
2235 return dynallocate<lst>({result, _ex1});
2236}
2237
2238
2241ex symbol::normal(exmap & repl, exmap & rev_lookup, lst & modifier) const
2242{
2243 return dynallocate<lst>({*this, _ex1});
2244}
2245
2246
2251ex numeric::normal(exmap & repl, exmap & rev_lookup, lst & modifier) const
2252{
2253 numeric num = numer();
2254 ex numex = num;
2255
2256 if (num.is_real()) {
2257 if (!num.is_integer())
2258 numex = replace_with_symbol(numex, repl, rev_lookup, modifier);
2259 } else { // complex
2260 numeric re = num.real(), im = num.imag();
2261 ex re_ex = re.is_rational() ? re : replace_with_symbol(re, repl, rev_lookup, modifier);
2262 ex im_ex = im.is_rational() ? im : replace_with_symbol(im, repl, rev_lookup, modifier);
2263 numex = re_ex + im_ex * replace_with_symbol(I, repl, rev_lookup, modifier);
2264 }
2265
2266 // Denominator is always a real integer (see numeric::denom())
2267 return dynallocate<lst>({numex, denom()});
2268}
2269
2270
2275static ex frac_cancel(const ex &n, const ex &d)
2276{
2277 ex num = n;
2278 ex den = d;
2279 numeric pre_factor = *_num1_p;
2280
2281//std::clog << "frac_cancel num = " << num << ", den = " << den << std::endl;
2282
2283 // Handle trivial case where denominator is 1
2284 if (den.is_equal(_ex1))
2285 return dynallocate<lst>({num, den});
2286
2287 // Handle special cases where numerator or denominator is 0
2288 if (num.is_zero())
2289 return dynallocate<lst>({num, _ex1});
2290 if (den.expand().is_zero())
2291 throw(std::overflow_error("frac_cancel: division by zero in frac_cancel"));
2292
2293 // Bring numerator and denominator to Z[X] by multiplying with
2294 // LCM of all coefficients' denominators
2297 num = multiply_lcm(num, num_lcm);
2298 den = multiply_lcm(den, den_lcm);
2299 pre_factor = den_lcm / num_lcm;
2300
2301 // Cancel GCD from numerator and denominator
2302 ex cnum, cden;
2303 if (gcd(num, den, &cnum, &cden, false) != _ex1) {
2304 num = cnum;
2305 den = cden;
2306 }
2307
2308 // Make denominator unit normal (i.e. coefficient of first symbol
2309 // as defined by get_first_symbol() is made positive)
2310 if (is_exactly_a<numeric>(den)) {
2311 if (ex_to<numeric>(den).is_negative()) {
2312 num *= _ex_1;
2313 den *= _ex_1;
2314 }
2315 } else {
2316 ex x;
2317 if (get_first_symbol(den, x)) {
2318 GINAC_ASSERT(is_exactly_a<numeric>(den.unit(x)));
2319 if (ex_to<numeric>(den.unit(x)).is_negative()) {
2320 num *= _ex_1;
2321 den *= _ex_1;
2322 }
2323 }
2324 }
2325
2326 // Return result as list
2327//std::clog << " returns num = " << num << ", den = " << den << ", pre_factor = " << pre_factor << std::endl;
2328 return dynallocate<lst>({num * pre_factor.numer(), den * pre_factor.denom()});
2329}
2330
2331
2335ex add::normal(exmap & repl, exmap & rev_lookup, lst & modifier) const
2336{
2337 // Normalize children and split each one into numerator and denominator
2338 exvector nums, dens;
2339 nums.reserve(seq.size()+1);
2340 dens.reserve(seq.size()+1);
2341 size_t nmod = modifier.nops(); // To watch new modifiers to the replacement list
2342 for (auto & it : seq) {
2343 ex n = ex_to<basic>(recombine_pair_to_ex(it)).normal(repl, rev_lookup, modifier);
2344 nums.push_back(n.op(0));
2345 dens.push_back(n.op(1));
2346 }
2347 ex n = ex_to<numeric>(overall_coeff).normal(repl, rev_lookup, modifier);
2348 nums.push_back(n.op(0));
2349 dens.push_back(n.op(1));
2350 GINAC_ASSERT(nums.size() == dens.size());
2351
2352 // Now, nums is a vector of all numerators and dens is a vector of
2353 // all denominators
2354//std::clog << "add::normal uses " << nums.size() << " summands:\n";
2355
2356 // Add fractions sequentially
2357 auto num_it = nums.begin(), num_itend = nums.end();
2358 auto den_it = dens.begin(), den_itend = dens.end();
2359//std::clog << " num = " << *num_it << ", den = " << *den_it << std::endl;
2360 for (size_t imod = nmod; imod < modifier.nops(); ++imod) {
2361 while (num_it != num_itend) {
2362 *num_it = num_it->subs(modifier.op(imod), subs_options::no_pattern);
2363 ++num_it;
2364 *den_it = den_it->subs(modifier.op(imod), subs_options::no_pattern);
2365 ++den_it;
2366 }
2367 // Reset iterators for the next round
2368 num_it = nums.begin();
2369 den_it = dens.begin();
2370 }
2371
2372 ex num = *num_it++, den = *den_it++;
2373 while (num_it != num_itend) {
2374//std::clog << " num = " << *num_it << ", den = " << *den_it << std::endl;
2375 ex next_num = *num_it++, next_den = *den_it++;
2376
2377 // Trivially add sequences of fractions with identical denominators
2378 while ((den_it != den_itend) && next_den.is_equal(*den_it)) {
2379 next_num += *num_it;
2380 num_it++; den_it++;
2381 }
2382
2383 // Addition of two fractions, taking advantage of the fact that
2384 // the heuristic GCD algorithm computes the cofactors at no extra cost
2385 ex co_den1, co_den2;
2386 ex g = gcd(den, next_den, &co_den1, &co_den2, false);
2387 num = ((num * co_den2) + (next_num * co_den1)).expand();
2388 den *= co_den2; // this is the lcm(den, next_den)
2389 }
2390//std::clog << " common denominator = " << den << std::endl;
2391
2392 // Cancel common factors from num/den
2393 return frac_cancel(num, den);
2394}
2395
2396
2400ex mul::normal(exmap & repl, exmap & rev_lookup, lst & modifier) const
2401{
2402 // Normalize children, separate into numerator and denominator
2403 exvector num; num.reserve(seq.size());
2404 exvector den; den.reserve(seq.size());
2405 ex n;
2406 size_t nmod = modifier.nops(); // To watch new modifiers to the replacement list
2407 for (auto & it : seq) {
2408 n = ex_to<basic>(recombine_pair_to_ex(it)).normal(repl, rev_lookup, modifier);
2409 num.push_back(n.op(0));
2410 den.push_back(n.op(1));
2411 }
2412 n = ex_to<numeric>(overall_coeff).normal(repl, rev_lookup, modifier);
2413 num.push_back(n.op(0));
2414 den.push_back(n.op(1));
2415 auto num_it = num.begin(), num_itend = num.end();
2416 auto den_it = den.begin();
2417 for (size_t imod = nmod; imod < modifier.nops(); ++imod) {
2418 while (num_it != num_itend) {
2419 *num_it = num_it->subs(modifier.op(imod), subs_options::no_pattern);
2420 ++num_it;
2421 *den_it = den_it->subs(modifier.op(imod), subs_options::no_pattern);
2422 ++den_it;
2423 }
2424 num_it = num.begin();
2425 den_it = den.begin();
2426 }
2427
2428 // Perform fraction cancellation
2429 return frac_cancel(dynallocate<mul>(num), dynallocate<mul>(den));
2430}
2431
2432
2437ex power::normal(exmap & repl, exmap & rev_lookup, lst & modifier) const
2438{
2439 // Normalize basis and exponent (exponent gets reassembled)
2440 size_t nmod = modifier.nops(); // To watch new modifiers to the replacement list
2441 ex n_basis = ex_to<basic>(basis).normal(repl, rev_lookup, modifier);
2442 for (size_t imod = nmod; imod < modifier.nops(); ++imod)
2443 n_basis = n_basis.subs(modifier.op(imod), subs_options::no_pattern);
2444
2445 nmod = modifier.nops();
2446 ex n_exponent = ex_to<basic>(exponent).normal(repl, rev_lookup, modifier);
2447 for (size_t imod = nmod; imod < modifier.nops(); ++imod)
2448 n_exponent = n_exponent.subs(modifier.op(imod), subs_options::no_pattern);
2449 n_exponent = n_exponent.op(0) / n_exponent.op(1);
2450
2451 if (n_exponent.info(info_flags::integer)) {
2452
2453 if (n_exponent.info(info_flags::positive)) {
2454
2455 // (a/b)^n -> {a^n, b^n}
2456 return dynallocate<lst>({pow(n_basis.op(0), n_exponent), pow(n_basis.op(1), n_exponent)});
2457
2458 } else if (n_exponent.info(info_flags::negative)) {
2459
2460 // (a/b)^-n -> {b^n, a^n}
2461 return dynallocate<lst>({pow(n_basis.op(1), -n_exponent), pow(n_basis.op(0), -n_exponent)});
2462 }
2463
2464 } else {
2465
2466 if (n_exponent.info(info_flags::positive)) {
2467
2468 // (a/b)^x -> {sym((a/b)^x), 1}
2469 return dynallocate<lst>({replace_with_symbol(pow(n_basis.op(0) / n_basis.op(1), n_exponent), repl, rev_lookup, modifier), _ex1});
2470
2471 } else if (n_exponent.info(info_flags::negative)) {
2472
2473 if (n_basis.op(1).is_equal(_ex1)) {
2474
2475 // a^-x -> {1, sym(a^x)}
2476 return dynallocate<lst>({_ex1, replace_with_symbol(pow(n_basis.op(0), -n_exponent), repl, rev_lookup, modifier)});
2477
2478 } else {
2479
2480 // (a/b)^-x -> {sym((b/a)^x), 1}
2481 return dynallocate<lst>({replace_with_symbol(pow(n_basis.op(1) / n_basis.op(0), -n_exponent), repl, rev_lookup, modifier), _ex1});
2482 }
2483 }
2484 }
2485
2486 // (a/b)^x -> {sym((a/b)^x, 1}
2487 return dynallocate<lst>({replace_with_symbol(pow(n_basis.op(0) / n_basis.op(1), n_exponent), repl, rev_lookup, modifier), _ex1});
2488}
2489
2490
2494ex pseries::normal(exmap & repl, exmap & rev_lookup, lst & modifier) const
2495{
2496 epvector newseq;
2497 for (auto & it : seq) {
2498 ex restexp = it.rest.normal();
2499 if (!restexp.is_zero())
2500 newseq.push_back(expair(restexp, it.coeff));
2501 }
2502 ex n = pseries(relational(var,point), std::move(newseq));
2503 return dynallocate<lst>({replace_with_symbol(n, repl, rev_lookup, modifier), _ex1});
2504}
2505
2506
2519{
2520 exmap repl, rev_lookup;
2521 lst modifier;
2522
2523 ex e = bp->normal(repl, rev_lookup, modifier);
2524 GINAC_ASSERT(is_a<lst>(e));
2525
2526 // Re-insert replaced symbols
2527 if (!repl.empty()) {
2528 for(size_t i=0; i < modifier.nops(); ++i)
2529 e = e.subs(modifier.op(i), subs_options::no_pattern);
2530 e = e.subs(repl, subs_options::no_pattern);
2531 }
2532
2533 // Convert {numerator, denominator} form back to fraction
2534 return e.op(0) / e.op(1);
2535}
2536
2544{
2545 exmap repl, rev_lookup;
2546 lst modifier;
2547
2548 ex e = bp->normal(repl, rev_lookup, modifier);
2549 GINAC_ASSERT(is_a<lst>(e));
2550
2551 // Re-insert replaced symbols
2552 if (repl.empty())
2553 return e.op(0);
2554 else {
2555 for(size_t i=0; i < modifier.nops(); ++i)
2556 e = e.subs(modifier.op(i), subs_options::no_pattern);
2557
2558 return e.op(0).subs(repl, subs_options::no_pattern);
2559 }
2560}
2561
2569{
2570 exmap repl, rev_lookup;
2571 lst modifier;
2572
2573 ex e = bp->normal(repl, rev_lookup, modifier);
2574 GINAC_ASSERT(is_a<lst>(e));
2575
2576 // Re-insert replaced symbols
2577 if (repl.empty())
2578 return e.op(1);
2579 else {
2580 for(size_t i=0; i < modifier.nops(); ++i)
2581 e = e.subs(modifier.op(i), subs_options::no_pattern);
2582
2583 return e.op(1).subs(repl, subs_options::no_pattern);
2584 }
2585}
2586
2594{
2595 exmap repl, rev_lookup;
2596 lst modifier;
2597
2598 ex e = bp->normal(repl, rev_lookup, modifier);
2599 GINAC_ASSERT(is_a<lst>(e));
2600
2601 // Re-insert replaced symbols
2602 if (repl.empty())
2603 return e;
2604 else {
2605 for(size_t i=0; i < modifier.nops(); ++i)
2606 e = e.subs(modifier.op(i), subs_options::no_pattern);
2607
2608 return e.subs(repl, subs_options::no_pattern);
2609 }
2610}
2611
2612
2627{
2628 return bp->to_rational(repl);
2629}
2630
2632{
2633 return bp->to_polynomial(repl);
2634}
2635
2639{
2640 return replace_with_symbol(*this, repl);
2641}
2642
2644{
2645 return replace_with_symbol(*this, repl);
2646}
2647
2648
2652{
2653 return *this;
2654}
2655
2659{
2660 return *this;
2661}
2662
2663
2668{
2669 if (is_real()) {
2670 if (!is_rational())
2671 return replace_with_symbol(*this, repl);
2672 } else { // complex
2673 numeric re = real();
2674 numeric im = imag();
2675 ex re_ex = re.is_rational() ? re : replace_with_symbol(re, repl);
2676 ex im_ex = im.is_rational() ? im : replace_with_symbol(im, repl);
2677 return re_ex + im_ex * replace_with_symbol(I, repl);
2678 }
2679 return *this;
2680}
2681
2686{
2687 if (is_real()) {
2688 if (!is_integer())
2689 return replace_with_symbol(*this, repl);
2690 } else { // complex
2691 numeric re = real();
2692 numeric im = imag();
2693 ex re_ex = re.is_integer() ? re : replace_with_symbol(re, repl);
2694 ex im_ex = im.is_integer() ? im : replace_with_symbol(im, repl);
2695 return re_ex + im_ex * replace_with_symbol(I, repl);
2696 }
2697 return *this;
2698}
2699
2700
2704{
2706 return pow(basis.to_rational(repl), exponent);
2707 else
2708 return replace_with_symbol(*this, repl);
2709}
2710
2714{
2716 return pow(basis.to_polynomial(repl), exponent);
2718 {
2719 ex basis_pref = collect_common_factors(basis);
2720 if (is_exactly_a<mul>(basis_pref) || is_exactly_a<power>(basis_pref)) {
2721 // (A*B)^n will be automagically transformed to A^n*B^n
2722 ex t = pow(basis_pref, exponent);
2723 return t.to_polynomial(repl);
2724 }
2725 else
2726 return pow(replace_with_symbol(pow(basis, _ex_1), repl), -exponent);
2727 }
2728 else
2729 return replace_with_symbol(*this, repl);
2730}
2731
2732
2735{
2736 epvector s;
2737 s.reserve(seq.size());
2738 for (auto & it : seq)
2739 s.push_back(split_ex_to_pair(recombine_pair_to_ex(it).to_rational(repl)));
2740
2741 ex oc = overall_coeff.to_rational(repl);
2742 if (oc.info(info_flags::numeric))
2743 return thisexpairseq(std::move(s), overall_coeff);
2744 else
2745 s.push_back(expair(oc, _ex1));
2746 return thisexpairseq(std::move(s), default_overall_coeff());
2747}
2748
2751{
2752 epvector s;
2753 s.reserve(seq.size());
2754 for (auto & it : seq)
2755 s.push_back(split_ex_to_pair(recombine_pair_to_ex(it).to_polynomial(repl)));
2756
2757 ex oc = overall_coeff.to_polynomial(repl);
2758 if (oc.info(info_flags::numeric))
2759 return thisexpairseq(std::move(s), overall_coeff);
2760 else
2761 s.push_back(expair(oc, _ex1));
2762 return thisexpairseq(std::move(s), default_overall_coeff());
2763}
2764
2765
2769static ex find_common_factor(const ex & e, ex & factor, exmap & repl)
2770{
2771 if (is_exactly_a<add>(e)) {
2772
2773 size_t num = e.nops();
2774 exvector terms; terms.reserve(num);
2775 ex gc;
2776
2777 // Find the common GCD
2778 for (size_t i=0; i<num; i++) {
2779 ex x = e.op(i).to_polynomial(repl);
2780
2781 if (is_exactly_a<add>(x) || is_exactly_a<mul>(x) || is_a<power>(x)) {
2782 ex f = 1;
2783 x = find_common_factor(x, f, repl);
2784 x *= f;
2785 }
2786
2787 if (gc.is_zero())
2788 gc = x;
2789 else
2790 gc = gcd(gc, x);
2791
2792 terms.push_back(x);
2793 }
2794
2795 if (gc.is_equal(_ex1))
2796 return e;
2797
2798 if (gc.is_zero())
2799 return _ex0;
2800
2801 // The GCD is the factor we pull out
2802 factor *= gc;
2803
2804 // Now divide all terms by the GCD
2805 for (size_t i=0; i<num; i++) {
2806 ex x;
2807
2808 // Try to avoid divide() because it expands the polynomial
2809 ex &t = terms[i];
2810 if (is_exactly_a<mul>(t)) {
2811 for (size_t j=0; j<t.nops(); j++) {
2812 if (t.op(j).is_equal(gc)) {
2813 exvector v; v.reserve(t.nops());
2814 for (size_t k=0; k<t.nops(); k++) {
2815 if (k == j)
2816 v.push_back(_ex1);
2817 else
2818 v.push_back(t.op(k));
2819 }
2820 t = dynallocate<mul>(v);
2821 goto term_done;
2822 }
2823 }
2824 }
2825
2826 divide(t, gc, x);
2827 t = x;
2828term_done: ;
2829 }
2830 return dynallocate<add>(terms);
2831
2832 } else if (is_exactly_a<mul>(e)) {
2833
2834 size_t num = e.nops();
2835 exvector v; v.reserve(num);
2836
2837 for (size_t i=0; i<num; i++)
2838 v.push_back(find_common_factor(e.op(i), factor, repl));
2839
2840 return dynallocate<mul>(v);
2841
2842 } else if (is_exactly_a<power>(e)) {
2843 const ex e_exp(e.op(1));
2844 if (e_exp.info(info_flags::integer)) {
2845 ex eb = e.op(0).to_polynomial(repl);
2846 ex factor_local(_ex1);
2847 ex pre_res = find_common_factor(eb, factor_local, repl);
2848 factor *= pow(factor_local, e_exp);
2849 return pow(pre_res, e_exp);
2850
2851 } else
2852 return e.to_polynomial(repl);
2853
2854 } else
2855 return e;
2856}
2857
2858
2862{
2863 if (is_exactly_a<add>(e) || is_exactly_a<mul>(e) || is_exactly_a<power>(e)) {
2864
2865 exmap repl;
2866 ex factor = 1;
2867 ex r = find_common_factor(e, factor, repl);
2869
2870 } else
2871 return e;
2872}
2873
2874
2877ex resultant(const ex & e1, const ex & e2, const ex & s)
2878{
2879 const ex ee1 = e1.expand();
2880 const ex ee2 = e2.expand();
2881 if (!ee1.info(info_flags::polynomial) ||
2883 throw(std::runtime_error("resultant(): arguments must be polynomials"));
2884
2885 const int h1 = ee1.degree(s);
2886 const int l1 = ee1.ldegree(s);
2887 const int h2 = ee2.degree(s);
2888 const int l2 = ee2.ldegree(s);
2889
2890 const int msize = h1 + h2;
2891 matrix m(msize, msize);
2892
2893 for (int l = h1; l >= l1; --l) {
2894 const ex e = ee1.coeff(s, l);
2895 for (int k = 0; k < h2; ++k)
2896 m(k, k+h1-l) = e;
2897 }
2898 for (int l = h2; l >= l2; --l) {
2899 const ex e = ee2.coeff(s, l);
2900 for (int k = 0; k < h1; ++k)
2901 m(k+h2, k+h2-l) = e;
2902 }
2903
2904 return m.determinant();
2905}
2906
2907
2908} // namespace GiNaC
Interface to GiNaC's sums of expressions.
#define GINAC_ASSERT(X)
Assertion macro for checking invariances.
Definition assertion.h:32
Interface to GiNaC's ABC.
ex coeff(const ex &s, int n=1) const override
Return coefficient of degree n in object s.
Definition add.cpp:291
numeric integer_content() const override
Definition normal.cpp:332
numeric max_coefficient() const override
Implementation ex::max_coefficient().
Definition normal.cpp:1170
ex expand(unsigned options=0) const override
Expand expression, i.e.
Definition add.cpp:569
ex normal(exmap &repl, exmap &rev_lookup, lst &modifier) const override
Implementation of ex::normal() for a sum.
Definition normal.cpp:2335
ex recombine_pair_to_ex(const expair &p) const override
Form an ex out of an expair, using the corresponding semantics.
Definition add.cpp:561
ex smod(const numeric &xi) const override
Apply symmetric modular homomorphism to an expanded multivariate polynomial.
Definition normal.cpp:1212
virtual size_t nops() const
Number of operands/members.
Definition basic.cpp:228
const basic & clearflag(unsigned f) const
Clear some status_flags.
Definition basic.h:290
virtual numeric integer_content() const
Definition normal.cpp:322
virtual numeric max_coefficient() const
Implementation ex::max_coefficient().
Definition normal.cpp:1160
virtual ex to_polynomial(exmap &repl) const
Definition normal.cpp:2643
virtual ex to_rational(exmap &repl) const
Default implementation of ex::to_rational().
Definition normal.cpp:2638
virtual ex coeff(const ex &s, int n=1) const
Return coefficient of degree n in object s.
Definition basic.cpp:336
virtual ex smod(const numeric &xi) const
Apply symmetric modular homomorphism to an expanded multivariate polynomial.
Definition normal.cpp:1202
virtual ex normal(exmap &repl, exmap &rev_lookup, lst &modifier) const
Default implementation of ex::normal().
Definition normal.cpp:2217
virtual ex map(map_function &f) const
Construct new expression by applying the specified function to all sub-expressions (one level only,...
Definition basic.cpp:293
Wrapper template for making GiNaC classes out of STL containers.
Definition container.h:72
size_t nops() const override
Number of operands/members.
Definition container.h:117
ex op(size_t i) const override
Return operand/member at position i.
Definition container.h:294
container & remove_first()
Remove first element.
Definition container.h:399
container & append(const ex &b)
Add element at back.
Definition container.h:390
Lightweight wrapper for GiNaC's symbolic objects.
Definition ex.h:72
ex to_rational(exmap &repl) const
Rationalization of non-rational functions.
Definition normal.cpp:2626
ex unit(const ex &x) const
Compute unit part (= sign of leading coefficient) of a multivariate polynomial in Q[x].
Definition normal.cpp:922
numeric integer_content() const
Compute the integer content (= GCD of all numeric coefficients) of an expanded polynomial.
Definition normal.cpp:317
ex primpart(const ex &x) const
Compute primitive part of a multivariate polynomial in Q[x].
Definition normal.cpp:980
ex lcoeff(const ex &s) const
Definition ex.h:176
const_iterator begin() const noexcept
Definition ex.h:662
bool find(const ex &pattern, exset &found) const
Find all occurrences of a pattern.
Definition ex.cpp:105
ex diff(const symbol &s, unsigned nth=1) const
Compute partial derivative of an expression.
Definition ex.cpp:86
ex expand(unsigned options=0) const
Expand an expression.
Definition ex.cpp:73
ex numer_denom() const
Get numerator and denominator of an expression.
Definition normal.cpp:2593
bool is_equal(const ex &other) const
Definition ex.h:345
ex normal() const
Normalization of rational functions.
Definition normal.cpp:2518
int degree(const ex &s) const
Definition ex.h:173
ptr< basic > bp
pointer to basic object managed by this
Definition ex.h:251
numeric max_coefficient() const
Return maximum (absolute value) coefficient of a polynomial.
Definition normal.cpp:1153
ex to_polynomial(exmap &repl) const
Definition normal.cpp:2631
void unitcontprim(const ex &x, ex &u, ex &c, ex &p) const
Compute unit part, content part, and primitive part of a multivariate polynomial in Q[x].
Definition normal.cpp:1021
size_t nops() const
Definition ex.h:135
ex smod(const numeric &xi) const
Definition ex.h:202
ex subs(const exmap &m, unsigned options=0) const
Definition ex.h:841
bool info(unsigned inf) const
Definition ex.h:132
ex denom() const
Get denominator of an expression.
Definition normal.cpp:2568
bool is_zero() const
Definition ex.h:213
ex op(size_t i) const
Definition ex.h:136
ex content(const ex &x) const
Compute content part (= unit normal GCD of all coefficients) of a multivariate polynomial in Q[x].
Definition normal.cpp:944
int ldegree(const ex &s) const
Definition ex.h:174
ex numer() const
Get numerator of an expression.
Definition normal.cpp:2543
ex coeff(const ex &s, int n=1) const
Definition ex.h:175
A pair of expressions.
Definition expair.h:37
ex to_polynomial(exmap &repl) const override
Implementation of ex::to_polynomial() for expairseqs.
Definition normal.cpp:2750
virtual ex default_overall_coeff() const
virtual ex thisexpairseq(const epvector &v, const ex &oc, bool do_index_renaming=false) const
Create an object of this type.
ex to_rational(exmap &repl) const override
Implementation of ex::to_rational() for expairseqs.
Definition normal.cpp:2734
virtual ex recombine_pair_to_ex(const expair &p) const
Form an ex out of an expair, using the corresponding semantics.
virtual expair split_ex_to_pair(const ex &e) const
Form an expair from an ex, using the corresponding semantics.
Exception thrown by heur_gcd() to signal failure.
Definition normal.cpp:1258
Symbolic matrices.
Definition matrix.h:37
matrix solve(const matrix &vars, const matrix &rhs, unsigned algo=solve_algo::automatic) const
Solve a linear system consisting of a m x n matrix and a m x p right hand side by applying an elimina...
Definition matrix.cpp:992
Product of expressions.
Definition mul.h:31
ex recombine_pair_to_ex(const expair &p) const override
Form an ex out of an expair, using the corresponding semantics.
Definition mul.cpp:997
ex smod(const numeric &xi) const override
Apply symmetric modular homomorphism to an expanded multivariate polynomial.
Definition normal.cpp:1227
ex normal(exmap &repl, exmap &rev_lookup, lst &modifier) const override
Implementation of ex::normal() for a product.
Definition normal.cpp:2400
numeric integer_content() const override
Definition normal.cpp:347
numeric max_coefficient() const override
Implementation ex::max_coefficient().
Definition normal.cpp:1184
This class is a wrapper around CLN-numbers within the GiNaC class hierarchy.
Definition numeric.h:81
ex to_rational(exmap &repl) const override
Implementation of ex::to_rational() for a numeric.
Definition normal.cpp:2667
numeric integer_content() const override
Definition normal.cpp:327
bool is_rational() const
True if object is an exact rational number, may even be complex (denominator may be unity).
Definition numeric.cpp:1200
const numeric real() const
Real part of a number.
Definition numeric.cpp:1338
ex to_polynomial(exmap &repl) const override
Implementation of ex::to_polynomial() for a numeric.
Definition normal.cpp:2685
bool is_real() const
True if object is a real integer, rational or float (but not complex).
Definition numeric.cpp:1207
const numeric numer() const
Numerator.
Definition numeric.cpp:1355
bool is_integer() const
True if object is a non-complex integer.
Definition numeric.cpp:1153
ex normal(exmap &repl, exmap &rev_lookup, lst &modifier) const override
Implementation of ex::normal() for a numeric.
Definition normal.cpp:2251
const numeric denom() const
Denominator.
Definition numeric.cpp:1386
const numeric imag() const
Imaginary part of a number.
Definition numeric.cpp:1345
numeric max_coefficient() const override
Implementation ex::max_coefficient().
Definition normal.cpp:1165
int int_length() const
Size in binary notation.
Definition numeric.cpp:1417
ex smod(const numeric &xi) const override
Apply symmetric modular homomorphism to an expanded multivariate polynomial.
Definition normal.cpp:1207
const numeric inverse() const
Inverse of a number.
Definition numeric.cpp:1052
bool is_zero() const
True if object is zero.
Definition numeric.cpp:1128
This class holds a two-component object, a basis and and exponent representing exponentiation.
Definition power.h:38
ex normal(exmap &repl, exmap &rev_lookup, lst &modifier) const override
Implementation of ex::normal() for powers.
Definition normal.cpp:2437
ex to_rational(exmap &repl) const override
Implementation of ex::to_rational() for powers.
Definition normal.cpp:2703
ex exponent
Definition power.h:105
ex to_polynomial(exmap &repl) const override
Implementation of ex::to_polynomial() for powers.
Definition normal.cpp:2713
This class holds a extended truncated power series (positive and negative integer powers).
Definition pseries.h:35
ex var
Series variable (holds a symbol)
Definition pseries.h:117
epvector seq
Vector of {coefficient, power} pairs.
Definition pseries.h:114
ex point
Expansion point.
Definition pseries.h:120
ex normal(exmap &repl, exmap &rev_lookup, lst &modifier) const override
Implementation of ex::normal() for pseries.
Definition normal.cpp:2494
This class holds a relation consisting of two expressions and a logical relation between them.
Definition relational.h:34
@ evaluated
.eval() has already done its job
Definition flags.h:202
@ hash_calculated
.calchash() has already done its job
Definition flags.h:204
@ no_pattern
disable pattern matching
Definition flags.h:50
Basic CAS symbol.
Definition symbol.h:38
ex to_rational(exmap &repl) const override
Implementation of ex::to_rational() for symbols.
Definition normal.cpp:2651
ex normal(exmap &repl, exmap &rev_lookup, lst &modifier) const override
Implementation of ex::normal() for symbols.
Definition normal.cpp:2241
ex to_polynomial(exmap &repl) const override
Implementation of ex::to_polynomial() for symbols.
Definition normal.cpp:2658
Interface to GiNaC's constant types and some special constants.
Interface to GiNaC's light-weight expression handles.
Interface to sequences of expression pairs.
ex unit
Definition factor.cpp:2190
upvec factors
Definition factor.cpp:1429
unsigned options
Definition factor.cpp:2473
vector< int > k
Definition factor.cpp:1434
size_t n
Definition factor.cpp:1431
size_t c
Definition factor.cpp:756
ex x
Definition factor.cpp:1609
size_t r
Definition factor.cpp:756
mvec m
Definition factor.cpp:757
ex cont
Definition factor.cpp:2190
Interface to class signaling failure of operation.
#define is_ex_the_function(OBJ, FUNCNAME)
Definition function.h:764
Interface to GiNaC's initially known functions.
Definition of GiNaC's lst.
Interface to symbolic matrices.
Interface to GiNaC's products of expressions.
Definition add.cpp:35
const numeric I
Imaginary unit.
Definition numeric.cpp:1432
ex denom(const ex &thisex)
Definition ex.h:763
const numeric pow(const numeric &x, const numeric &y)
Definition numeric.h:250
static ex replace_with_symbol(const ex &e, exmap &repl, exmap &rev_lookup, lst &modifier)
Create a symbol for replacing the expression "e" (or return a previously assigned symbol).
Definition normal.cpp:2077
std::map< ex, ex, ex_is_less > exmap
Definition basic.h:49
ex sqrfree(const ex &a, const lst &l)
Compute a square-free factorization of a multivariate polynomial in Q[X].
Definition normal.cpp:1888
std::vector< expair > epvector
expair-vector
Definition expairseq.h:32
ex resultant(const ex &e1, const ex &e2, const ex &s)
Resultant of two expressions e1,e2 with respect to symbol s.
Definition normal.cpp:2877
const numeric abs(const numeric &x)
Absolute value.
Definition numeric.cpp:2319
static ex interpolate(const ex &gamma, const numeric &xi, const ex &x, int degree_hint=1)
xi-adic polynomial interpolation
Definition normal.cpp:1244
static void add_symbol(const ex &s, sym_desc_vec &v)
Definition normal.cpp:162
bool is_negative(const numeric &x)
Definition numeric.h:268
const ex _ex1
Definition utils.cpp:384
bool is_rational(const numeric &x)
Definition numeric.h:289
ex gcd(const ex &a, const ex &b, ex *ca, ex *cb, bool check_args, unsigned options)
Compute GCD (Greatest Common Divisor) of multivariate polynomials a(X) and b(X) in Z[X].
Definition normal.cpp:1432
static ex find_common_factor(const ex &e, ex &factor, exmap &repl)
Remove the common factor in the terms of a sum 'e' by calculating the GCD, and multiply it into the e...
Definition normal.cpp:2769
function psi(const T1 &p1)
Definition inifcns.h:164
ex rhs(const ex &thisex)
Definition ex.h:832
ex prem(const ex &a, const ex &b, const ex &x, bool check_args)
Pseudo-remainder of polynomials a(x) and b(x) in Q[x].
Definition normal.cpp:491
static ex gcd_pf_pow_pow(const ex &a, const ex &b, ex *ca, ex *cb)
Definition normal.cpp:1646
static epvector sqrfree_yun(const ex &a, const symbol &x)
Compute square-free factorization of multivariate polynomial a(x) using Yun's algorithm.
Definition normal.cpp:1798
const numeric exp(const numeric &x)
Exponential function.
Definition numeric.cpp:1438
ex quo(const ex &a, const ex &b, const ex &x, bool check_args)
Quotient q(x) of polynomials a(x) and b(x) in Q[x].
Definition normal.cpp:372
static ex sr_gcd(const ex &a, const ex &b, sym_desc_vec::const_iterator var)
Compute GCD of multivariate polynomials using the subresultant PRS algorithm.
Definition normal.cpp:1080
static bool get_first_symbol(const ex &e, ex &x)
Return pointer to first symbol found in expression.
Definition normal.cpp:94
const ex _ex_1
Definition utils.cpp:351
const numeric smod(const numeric &a_, const numeric &b_)
Modulus (in symmetric representation).
Definition numeric.cpp:2345
int degree(const ex &thisex, const ex &s)
Definition ex.h:751
static ex gcd_pf_mul(const ex &a, const ex &b, ex *ca, ex *cb)
Definition normal.cpp:1740
ex sqrfree_parfrac(const ex &a, const symbol &x)
Compute square-free partial fraction decomposition of rational function a(x).
Definition normal.cpp:1947
ex lcm(const ex &a, const ex &b, bool check_args)
Compute LCM (Least Common Multiple) of multivariate polynomials in Z[X].
Definition normal.cpp:1774
const numeric iquo(const numeric &a, const numeric &b)
Numeric integer quotient.
Definition numeric.cpp:2408
static bool divide_in_z(const ex &a, const ex &b, ex &q, sym_desc_vec::const_iterator var)
Exact polynomial division of a(X) by b(X) in Z[X].
Definition normal.cpp:756
const numeric isqrt(const numeric &x)
Integer numeric square root.
Definition numeric.cpp:2486
ex normal(const ex &thisex)
Definition ex.h:769
ex decomp_rational(const ex &a, const ex &x)
Decompose rational function a(x)=N(x)/D(x) into P(x)+n(x)/D(x) with degree(n, x) < degree(D,...
Definition normal.cpp:471
static bool heur_gcd(ex &res, const ex &a, const ex &b, ex *ca, ex *cb, sym_desc_vec::const_iterator var)
Compute GCD of multivariate polynomials using the heuristic GCD algorithm.
Definition normal.cpp:1370
const numeric * _num1_p
Definition utils.cpp:383
ex coeff(const ex &thisex, const ex &s, int n=1)
Definition ex.h:757
static ex multiply_lcm(const ex &e, const numeric &lcm)
Bring polynomial from Q[X] to Z[X] by multiplying in the previously determined LCM of the coefficient...
Definition normal.cpp:270
static bool heur_gcd_z(ex &res, const ex &a, const ex &b, ex *ca, ex *cb, sym_desc_vec::const_iterator var)
Compute GCD of multivariate polynomials using the heuristic GCD algorithm.
Definition normal.cpp:1276
ex numer(const ex &thisex)
Definition ex.h:760
ex factor(const ex &poly, unsigned options)
Interface function to the outside world.
Definition factor.cpp:2574
int to_int(const numeric &x)
Definition numeric.h:301
ex collect_common_factors(const ex &e)
Collect common factors in sums.
Definition normal.cpp:2861
static numeric lcm_of_coefficients_denominators(const ex &e)
Compute LCM of denominators of coefficients of a polynomial.
Definition normal.cpp:260
bool is_integer(const numeric &x)
Definition numeric.h:271
static numeric lcmcoeff(const ex &e, const numeric &l)
Definition normal.cpp:230
static void get_symbol_stats(const ex &a, const ex &b, sym_desc_vec &v)
Collect statistical information about symbols in polynomials.
Definition normal.cpp:196
ex rem(const ex &a, const ex &b, const ex &x, bool check_args)
Remainder r(x) of polynomials a(x) and b(x) in Q[x].
Definition normal.cpp:422
ex sprem(const ex &a, const ex &b, const ex &x, bool check_args)
Sparse pseudo-remainder of polynomials a(x) and b(x) in Q[x].
Definition normal.cpp:543
std::vector< sym_desc > sym_desc_vec
Definition normal.cpp:159
static ex frac_cancel(const ex &n, const ex &d)
Fraction cancellation.
Definition normal.cpp:2275
bool divide(const ex &a, const ex &b, ex &q, bool check_args)
Exact polynomial division of a(X) by b(X) in Q[X].
Definition normal.cpp:594
static void collect_symbols(const ex &e, sym_desc_vec &v)
Definition normal.cpp:172
const ex _ex0
Definition utils.cpp:368
std::vector< ex > exvector
Definition basic.h:47
ex numer_denom(const ex &thisex)
Definition ex.h:766
static ex gcd_pf_pow(const ex &a, const ex &b, ex *ca, ex *cb)
Definition normal.cpp:1693
const numeric * _num0_p
Definition utils.cpp:366
ex expand(const ex &thisex, unsigned options=0)
Definition ex.h:730
This file defines several functions that work on univariate and multivariate polynomials and rational...
Makes the interface to the underlying bignum package available.
Interface to GiNaC's overloaded operators.
Interface to GiNaC's symbolic exponentiation (basis^exponent).
Interface to class for extended truncated power series.
Interface to relations between expressions.
@ use_sr_gcd
By default GiNaC uses modular GCD algorithm.
Definition normal.h:60
@ no_part_factored
GiNaC tries to avoid expanding expressions when computing GCDs.
Definition normal.h:54
@ no_heur_gcd
Usually GiNaC tries heuristic GCD first, because typically it's much faster than anything else.
Definition normal.h:46
Function object for map().
Definition basic.h:84
Function object to be applied by basic::normal().
Definition normal.cpp:2210
ex operator()(const ex &e) override
Definition normal.cpp:2211
This structure holds information about the highest and lowest degrees in which a symbol appears in tw...
Definition normal.cpp:121
int max_deg
Maximum of deg_a and deg_b (Used for sorting)
Definition normal.cpp:143
int ldeg_a
Lowest degree of symbol in polynomial "a".
Definition normal.cpp:137
ex sym
Reference to symbol.
Definition normal.cpp:128
int ldeg_b
Lowest degree of symbol in polynomial "b".
Definition normal.cpp:140
bool operator<(const sym_desc &x) const
Comparison operator for sorting.
Definition normal.cpp:149
int deg_b
Highest degree of symbol in polynomial "b".
Definition normal.cpp:134
int deg_a
Highest degree of symbol in polynomial "a".
Definition normal.cpp:131
size_t max_lcnops
Maximum number of terms of leading coefficient of symbol in both polynomials.
Definition normal.cpp:146
sym_desc(const ex &s)
Initialize symbol, leave other variables uninitialized.
Definition normal.cpp:123
Interface to GiNaC's symbolic objects.
Interface to several small and furry utilities needed within GiNaC but not of any interest to the use...

This page is part of the GiNaC developer's reference. It was generated automatically by doxygen. For an introduction, see the tutorial.