1 /** @file upoly-ginac.cpp
2  *
3  *  This file implements several functions that work on univariate and
4  *  rational functions.
5  *  These functions include polynomial quotient and remainder, GCD and LCM
6  *  computation, square-free factorization and rational function normalization. */
7 
8 /*
9  *  GiNaC Copyright (C) 1999-2008 Johannes Gutenberg University Mainz, Germany
10  *                  (C) 2016 Ralf Stephan
11  *
12  *  This program is free software; you can redistribute it and/or modify
13  *  it under the terms of the GNU General Public License as published by
14  *  the Free Software Foundation; either version 2 of the License, or
15  *  (at your option) any later version.
16  *
17  *  This program is distributed in the hope that it will be useful,
18  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
19  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  *  GNU General Public License for more details.
21  *
22  *  You should have received a copy of the GNU General Public License
23  *  along with this program; if not, write to the Free Software
24  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
25  */
26 
27 #ifdef HAVE_CONFIG_H
28 #include "pynac-config.h"
29 #endif
30 
31 #include "ex.h"
32 #include "ex_utils.h"
33 #include "numeric.h"
34 #include "normal.h"
35 #include "upoly.h"
36 #include "symbol.h"
37 #include "exprseq.h"
38 #include "add.h"
39 #include "mul.h"
40 #include "power.h"
41 #include "matrix.h"
42 #include "operators.h"
43 #include "utils.h"
44 
45 #include <map>
46 #include <sstream>
47 
48 namespace 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 /*
56  *  Polynomial quotients and remainders
57  */
58 
59 /** Quotient q(x) of polynomials a(x) and b(x) in Q[x].
60  *  It satisfies a(x)=b(x)*q(x)+r(x).
61  *
62  *  @param a  first polynomial in x (dividend)
63  *  @param b  second polynomial in x (divisor)
64  *  @param x  a and b are polynomials in x
65  *  @param check_args  check whether a and b are polynomials with rational
66  *         coefficients (defaults to "true")
67  *  @return quotient of a and b in Q[x] */
quo(const ex & a,const ex & b,const ex & x,bool check_args)68 ex quo(const ex &a, const ex &b, const ex &x, bool check_args)
69 {
70 	if (b.is_zero())
71 		throw(std::overflow_error("quo: division by zero"));
72 	if (is_exactly_a<numeric>(a) && is_exactly_a<numeric>(b))
73 		return a / b;
74 #if FAST_COMPARE
75 	if (a.is_equal(b))
76 		return _ex1;
77 #endif
78 	if (check_args && (!a.info(info_flags::rational_polynomial) || !b.info(info_flags::rational_polynomial)))
79 		throw(std::invalid_argument("quo: arguments must be polynomials over the rationals"));
80 	// Polynomial long division
81 	ex r = a.expand();
82 	if (r.is_zero())
83 		return r;
84 	numeric bdeg = b.degree(x);
85 	numeric rdeg = r.degree(x);
86 	ex blcoeff = b.expand().coeff(x, bdeg);
87 	bool blcoeff_is_numeric = is_exactly_a<numeric>(blcoeff);
88 	exvector v; //v.reserve(std::max(rdeg - bdeg + 1, 0));
89 	while (rdeg >= bdeg) {
90 		ex term, rcoeff = r.coeff(x, rdeg);
91 		if (blcoeff_is_numeric)
92 			term = rcoeff / blcoeff;
93 		else {
94 			if (!divide(rcoeff, blcoeff, term, false))
95                                 throw std::logic_error("");
96 		}
97 		term *= power(x, rdeg - bdeg);
98 		v.push_back(term);
99 		r -= (term * b).expand();
100 		if (r.is_zero())
101 			break;
102 		rdeg = r.degree(x);
103 	}
104         return (new add(v))->setflag(status_flags::dynallocated);
105 }
106 
107 /** Remainder r(x) of polynomials a(x) and b(x) in Q[x].
108  *  It satisfies a(x)=b(x)*q(x)+r(x).
109  *
110  *  @param a  first polynomial in x (dividend)
111  *  @param b  second polynomial in x (divisor)
112  *  @param x  a and b are polynomials in x
113  *  @param check_args  check whether a and b are polynomials with rational
114  *         coefficients (defaults to "true")
115  *  @return remainder of a(x) and b(x) in Q[x] */
rem(const ex & a,const ex & b,const ex & x,bool check_args)116 ex rem(const ex &a, const ex &b, const ex &x, bool check_args)
117 {
118 	if (b.is_zero())
119 		throw(std::overflow_error("rem: division by zero"));
120 	if (is_exactly_a<numeric>(a)) {
121 		if  (is_exactly_a<numeric>(b))
122 			return _ex0;
123 		return a;
124 	}
125 #if FAST_COMPARE
126 	if (a.is_equal(b))
127 		return _ex0;
128 #endif
129 	if (check_args && (!a.info(info_flags::rational_polynomial) || !b.info(info_flags::rational_polynomial)))
130 		throw(std::invalid_argument("rem: arguments must be polynomials over the rationals"));
131 
132 	// Polynomial long division
133 	ex r = a.expand();
134 	if (r.is_zero())
135 		return r;
136 	numeric bdeg = b.degree(x);
137         numeric rdeg = r.degree(x);
138 	ex blcoeff = b.expand().coeff(x, bdeg);
139 	bool blcoeff_is_numeric = is_exactly_a<numeric>(blcoeff);
140 	while (rdeg >= bdeg) {
141 		ex term, rcoeff = r.coeff(x, rdeg);
142 		if (blcoeff_is_numeric)
143 			term = rcoeff / blcoeff;
144 		else {
145 			if (!divide(rcoeff, blcoeff, term, false))
146                                 throw std::logic_error("");
147 		}
148 		term *= power(x, rdeg - bdeg);
149 		r -= (term * b).expand();
150 		if (r.is_zero())
151 			break;
152 		rdeg = r.degree(x);
153 	}
154 	return r;
155 }
156 
157 
quo_rem(const ex & a,const ex & b,const ex & x,bool check_args)158 std::pair<ex,ex> quo_rem(const ex &a, const ex &b, const ex &x, bool check_args)
159 {
160 	if (b.is_zero())
161 		throw(std::overflow_error("quo: division by zero"));
162 	if (is_exactly_a<numeric>(a) && is_exactly_a<numeric>(b))
163 		return std::make_pair(a / b, _ex0);
164 #if FAST_COMPARE
165 	if (a.is_equal(b))
166 		return std::make_pair(_ex1, _ex0);
167 #endif
168         expairvec vec1, vec2;
169         using pair_t = std::pair<ex,ex>;
170         a.expand().coefficients(x, vec1);
171         b.expand().coefficients(x, vec2);
172         if (check_args) {
173                 if (std::any_of(vec1.begin(), vec1.end(),
174                              [](const pair_t& p) {
175                                 return not is_exactly_a<numeric>(p.second)
176                                     or (not p.second.is_zero()
177                                         and not p.second.info(info_flags::posint)); }
178                                     ))
179                         throw std::logic_error("nonposint exponent in quo()");
180                 if (std::any_of(vec2.begin(), vec2.end(),
181                              [](const pair_t& p) {
182                                 return not is_exactly_a<numeric>(p.second)
183                                     or (not p.second.is_zero()
184                                         and not p.second.info(info_flags::posint)); }
185                                     ))
186                         throw std::logic_error("nonposint exponent in quo()");
187         }
188         const auto& mit1 = std::max_element(vec1.begin(), vec1.end(),
189                         [](const pair_t& p1, const pair_t& p2) {
190                            return ex_to<numeric>(p1.second) <
191                                   ex_to<numeric>(p2.second); } );
192         const auto& mit2 = std::max_element(vec2.begin(), vec2.end(),
193                         [](const pair_t& p1, const pair_t& p2) {
194                            return ex_to<numeric>(p1.second) <
195                                   ex_to<numeric>(p2.second); } );
196         size_t adeg = ex_to<numeric>(mit1->second).to_long();
197         size_t bdeg = ex_to<numeric>(mit2->second).to_long();
198         if (adeg < bdeg)
199                 return std::make_pair(_ex0, a);
200         // make flat coeff vectors
201         std::vector<ex> avec, bvec;
202         avec.assign(adeg+1, _ex0);
203         bvec.assign(bdeg+1, _ex0);
204         for (const pair_t& p : vec1)
205                 *(avec.begin() + ex_to<numeric>(p.second).to_long()) = p.first;
206         for (const pair_t& p : vec2)
207                 *(bvec.begin() + ex_to<numeric>(p.second).to_long()) = p.first;
208 
209         // poly division
210         ex lc2 = bvec[bdeg];
211         exvector qvec;
212         for (int k=adeg-bdeg; k>=0; --k) {
213                 ex q = avec[bdeg+k] / lc2;
214                 avec[bdeg+k] = _ex0;
215                 for (int j=bdeg+k-1; j>=k; --j)
216                         avec[j] = avec[j] - q * bvec[j-k];
217                 qvec.insert(qvec.begin(), q);
218         }
219         for (size_t i=0; i<qvec.size(); ++i)
220                 if (not qvec[i].is_zero())
221                         qvec[i] = qvec[i] * power(x, numeric(i));
222         if (avec.size() > bdeg)
223                 avec.resize(bdeg);
224         for (size_t i=0; i<bdeg; ++i)
225                 if (not avec[i].is_zero())
226                         avec[i] = avec[i] * power(x, numeric(i));
227         return std::make_pair(add(qvec), add(avec));
228 }
229 
230 
231 /** Decompose rational function a(x)=N(x)/D(x) into P(x)+n(x)/D(x)
232  *  with degree(n, x) < degree(D, x).
233  *
234  *  @param a rational function in x
235  *  @param x a is a function of x
236  *  @return decomposed function. */
decomp_rational(const ex & a,const ex & x)237 ex decomp_rational(const ex &a, const ex &x)
238 {
239 	ex nd = numer_denom(a);
240 	ex numer = nd.op(0), denom = nd.op(1);
241         ex q;
242         try {
243         	q = quo(numer, denom, x);
244         }
245         catch (std::logic_error) {
246 		return a;
247         }
248 	return q + rem(numer, denom, x) / denom;
249 }
250 
251 
252 /** Pseudo-remainder of polynomials a(x) and b(x) in Q[x].
253  *
254  *  @param a  first polynomial in x (dividend)
255  *  @param b  second polynomial in x (divisor)
256  *  @param x  a and b are polynomials in x
257  *  @param check_args  check whether a and b are polynomials with rational
258  *         coefficients (defaults to "true")
259  *  @return pseudo-remainder of a(x) and b(x) in Q[x] */
prem(const ex & a,const ex & b,const ex & x,bool check_args)260 ex prem(const ex &a, const ex &b, const ex &x, bool check_args)
261 {
262 	if (b.is_zero())
263 		throw(std::overflow_error("prem: division by zero"));
264 	if (is_exactly_a<numeric>(a)) {
265 		if (is_exactly_a<numeric>(b))
266 			return _ex0;
267 		return b;
268 	}
269 	if (check_args && (!a.info(info_flags::rational_polynomial) || !b.info(info_flags::rational_polynomial)))
270 		throw(std::invalid_argument("prem: arguments must be polynomials over the rationals"));
271 
272 	// Polynomial long division
273 	ex r = a.expand();
274 	ex eb = b.expand();
275 	numeric rdeg = r.degree(x);
276 	numeric bdeg = eb.degree(x);
277 	ex blcoeff;
278 	if (bdeg <= rdeg) {
279 		blcoeff = eb.coeff(x, bdeg);
280 		if (bdeg == 0)
281 			eb = _ex0;
282 		else
283 			eb -= blcoeff * power(x, bdeg);
284 	} else
285 		blcoeff = _ex1;
286 
287 	numeric delta = rdeg - bdeg + 1, i = 0;
288 	while (rdeg >= bdeg && !r.is_zero()) {
289 		ex rlcoeff = r.coeff(x, rdeg);
290 		ex term = (power(x, rdeg - bdeg) * eb * rlcoeff).expand();
291 		if (rdeg == 0)
292 			r = _ex0;
293 		else
294 			r -= rlcoeff * power(x, rdeg);
295 		r = (blcoeff * r).expand() - term;
296 		rdeg = r.degree(x);
297 		i++;
298 	}
299 	return power(blcoeff, delta - i) * r;
300 }
301 
302 
303 /** Sparse pseudo-remainder of polynomials a(x) and b(x) in Q[x].
304  *
305  *  @param a  first polynomial in x (dividend)
306  *  @param b  second polynomial in x (divisor)
307  *  @param x  a and b are polynomials in x
308  *  @param check_args  check whether a and b are polynomials with rational
309  *         coefficients (defaults to "true")
310  *  @return sparse pseudo-remainder of a(x) and b(x) in Q[x] */
sprem(const ex & a,const ex & b,const ex & x,bool check_args)311 ex sprem(const ex &a, const ex &b, const ex &x, bool check_args)
312 {
313 	if (b.is_zero())
314 		throw(std::overflow_error("prem: division by zero"));
315 	if (is_exactly_a<numeric>(a)) {
316 		if (is_exactly_a<numeric>(b))
317 			return _ex0;
318 		return b;
319 	}
320 	if (check_args && (!a.info(info_flags::rational_polynomial) || !b.info(info_flags::rational_polynomial)))
321 		throw(std::invalid_argument("prem: arguments must be polynomials over the rationals"));
322 
323 	// Polynomial long division
324 	ex r = a.expand();
325 	ex eb = b.expand();
326 	numeric rdeg = r.degree(x);
327 	numeric bdeg = eb.degree(x);
328 	ex blcoeff;
329 	if (bdeg <= rdeg) {
330 		blcoeff = eb.coeff(x, bdeg);
331 		if (bdeg == 0)
332 			eb = _ex0;
333 		else
334 			eb -= blcoeff * power(x, bdeg);
335 	} else
336 		blcoeff = _ex1;
337 
338 	while (rdeg >= bdeg && !r.is_zero()) {
339 		ex rlcoeff = r.coeff(x, rdeg);
340 		ex term = (power(x, rdeg - bdeg) * eb * rlcoeff).expand();
341 		if (rdeg == 0)
342 			r = _ex0;
343 		else
344 			r -= rlcoeff * power(x, rdeg);
345 		r = (blcoeff * r).expand() - term;
346 		rdeg = r.degree(x);
347 	}
348 	return r;
349 }
350 
351 
352 /** Exact polynomial division of a(X) by b(X) in Q[X].
353  *
354  *  @param a  first multivariate polynomial (dividend)
355  *  @param b  second multivariate polynomial (divisor)
356  *  @param q  quotient (returned)
357  *  @param check_args  check whether a and b are polynomials with rational
358  *         coefficients (defaults to "true")
359  *  @return "true" when exact division succeeds (quotient returned in q),
360  *          "false" otherwise (q left untouched) */
divide(const ex & a,const ex & b,ex & q,bool check_args)361 bool divide(const ex &a, const ex &b, ex &q, bool check_args)
362 {
363 	if (b.is_zero())
364 		throw(std::overflow_error("divide: division by zero"));
365 	if (a.is_zero()) {
366 		q = _ex0;
367 		return true;
368 	}
369 	if (is_exactly_a<numeric>(b)) {
370 		q = a / b;
371 		return true;
372 	}
373         if (is_exactly_a<numeric>(a))
374 		return false;
375 #if FAST_COMPARE
376 	if (a.is_equal(b)) {
377 		q = _ex1;
378 		return true;
379 	}
380 #endif
381 	if (check_args && (!a.info(info_flags::rational_polynomial) ||
382 	                   !b.info(info_flags::rational_polynomial)))
383 		throw(std::invalid_argument("divide: arguments must be polynomials over the rationals"));
384 
385 	// Find first symbol
386 	ex x;
387 	if (!a.get_first_symbol(x) && !b.get_first_symbol(x)) {
388                 std::ostringstream os;
389                 os << "invalid expression in divide(): " << a << " / " << b;
390 		throw(std::invalid_argument(os.str()));
391         }
392 
393 	// Try to avoid expanding partially factored expressions.
394 	if (is_exactly_a<mul>(b)) {
395 	// Divide sequentially by each term
396 		ex rem_new, rem_old = a;
397 		for (size_t i=0; i < b.nops(); i++) {
398 			if (! divide(rem_old, b.op(i), rem_new, false))
399 				return false;
400 			rem_old = rem_new;
401 		}
402 		q = rem_new;
403 		return true;
404 	}
405         if (is_exactly_a<power>(b)) {
406 		const ex& bb(b.op(0));
407 		int exp_b = ex_to<numeric>(b.op(1)).to_int();
408 		ex rem_new, rem_old = a;
409 		for (int i=exp_b; i>0; i--) {
410 			if (! divide(rem_old, bb, rem_new, false))
411 				return false;
412 			rem_old = rem_new;
413 		}
414 		q = rem_new;
415 		return true;
416 	}
417 
418 	if (is_exactly_a<mul>(a)) {
419 		// Divide sequentially each term. If some term in a is divisible
420 		// by b we are done... and if not, we can't really say anything.
421 		size_t i;
422 		ex rem_i;
423 		bool divisible_p = false;
424 		for (i=0; i < a.nops(); ++i) {
425 			if (divide(a.op(i), b, rem_i, false)) {
426 				divisible_p = true;
427 				break;
428 			}
429 		}
430 		if (divisible_p) {
431 			exvector resv;
432 			resv.reserve(a.nops());
433 			for (size_t j=0; j < a.nops(); j++) {
434 				if (j==i)
435 					resv.push_back(rem_i);
436 				else
437 					resv.push_back(a.op(j));
438 			}
439 			q = (new mul(resv))->setflag(status_flags::dynallocated);
440 			return true;
441 		}
442 	} else if (is_exactly_a<power>(a)) {
443 		// The base itself might be divisible by b, in that case we don't
444 		// need to expand a
445 		const ex& ab(a.op(0));
446 		int a_exp = ex_to<numeric>(a.op(1)).to_int();
447 		ex rem_i;
448 		if (divide(ab, b, rem_i, false)) {
449 			q = rem_i*power(ab, a_exp - 1);
450 			return true;
451 		}
452 // code below is commented-out because it leads to a significant slowdown
453 //		for (int i=2; i < a_exp; i++) {
454 //			if (divide(power(ab, i), b, rem_i, false)) {
455 //				q = rem_i*power(ab, a_exp - i);
456 //				return true;
457 //			}
458 //		} // ... so we *really* need to expand expression.
459 	}
460 
461 	// Polynomial long division (recursive)
462 	ex r = a.expand();
463 	if (r.is_zero()) {
464 		q = _ex0;
465 		return true;
466 	}
467 	numeric bdeg = b.degree(x);
468 	numeric rdeg = r.degree(x);
469 	ex blcoeff = b.expand().coeff(x, bdeg);
470 	bool blcoeff_is_numeric = is_exactly_a<numeric>(blcoeff);
471 	exvector v; //v.reserve(std::max(rdeg - bdeg + 1, 0));
472 	while (rdeg >= bdeg) {
473 		ex term, rcoeff = r.coeff(x, rdeg);
474 		if (blcoeff_is_numeric)
475 			term = rcoeff / blcoeff;
476 		else
477 			if (!divide(rcoeff, blcoeff, term, false))
478 				return false;
479 		term *= power(x, rdeg - bdeg);
480 		v.push_back(term);
481 		r -= (term * b).expand();
482 		if (r.is_zero()) {
483 			q = (new add(v))->setflag(status_flags::dynallocated);
484 			return true;
485 		}
486 		rdeg = r.degree(x);
487 	}
488 	return false;
489 }
490 
491 // Distribute a factor over all sum terms
dist(const ex & f,const ex & p)492 static ex dist(const ex& f, const ex& p)
493 {
494         if (not is_exactly_a<add>(p))
495                 return mul(f, p);
496         ex s = _ex0;
497         for (size_t i=0; i<p.nops(); ++i)
498                 s += f*p.op(i);
499         return s;
500 }
501 
502 /** Compute partial fraction decomposition of expression
503  *
504  *  @param a expression
505  *  @param x variable to factor in
506  *  @return decomposed rational function */
parfrac(const ex & a,const ex & x)507 ex parfrac(const ex & a, const ex & x)
508 {
509         if (is_exactly_a<add>(a)) {
510                 ex s = _ex0;
511                 for (size_t i=0; i<a.nops(); ++i)
512                         s += parfrac(a.op(i), x);
513                 return s;
514         }
515         if (not is_exactly_a<mul>(a))
516                 return a;
517 	// Find numerator and denominator
518 	ex nd = numer_denom(a);
519 	ex numer = nd.op(0), denom = nd.op(1);
520         // Expand sum powers
521         if (is_exactly_a<add>(numer))
522                 return parfrac(dist(_ex1/denom, numer), x);
523         if (is_exactly_a<power>(numer)
524             and is_exactly_a<add>(numer.op(0))
525             and numer.op(1).info(info_flags::posint))
526                 return parfrac(dist(_ex1/denom, numer.expand()), x);
527 
528         if (is_exactly_a<mul>(numer))
529                 for (size_t i=0; i<numer.nops(); ++i) {
530                         const ex& t = numer.op(i);
531                         if (is_exactly_a<power>(t)
532                             and is_exactly_a<add>(t.op(0))
533                             and t.op(1).info(info_flags::posint))
534                                 return parfrac(dist(_ex1/denom, numer.expand()), x);
535                 }
536         std::pair<ex,ex> qr;
537         try {
538         	// Convert N(x)/D(x) -> Q(x) + R(x)/D(x), so degree(R) < degree(D)
539                 qr = quo_rem(numer, denom, x, true);
540         }
541         catch (std::logic_error) {
542                 return a;
543         }
544 	// Factorize denominator and compute cofactors
545         ex facmul;
546         bool r = factor(denom, facmul);
547         if (not r)
548                 facmul = denom;
549 //                return qr.first + qr.second/denom;
550         ex oc = _ex1;
551 	exvector factor, cofac;
552         if (is_exactly_a<mul>(facmul)) {
553                 size_t fsize = facmul.nops();
554                 for (size_t i=0; i<fsize; i++) {
555                         const ex& e = facmul.op(i);
556                         if (is_exactly_a<numeric>(e)) {
557                                 oc = e;
558                                 continue;
559                         }
560                         if (is_exactly_a<power>(e)) {
561                                 if (not has(e.op(0), x))
562                                         continue;
563                                 const ex& ee = e.op(1);
564                                 if (not is_exactly_a<numeric>(ee)
565                                     or not ee.is_integer()
566                                     or e.op(0).is_equal(x))
567                                         return a;
568                                 size_t expo = ex_to<numeric>(ee).to_int();
569                                 for (size_t j=1; j<=expo; ++j) {
570                                         ex eee = power(e.op(0), numeric(j));
571                                         factor.push_back(eee);
572                                         cofac.push_back((facmul/eee).expand());
573                                 }
574                         }
575                         else {
576                                 factor.push_back(e);
577                                 cofac.push_back((facmul/e).expand());
578                         }
579                 }
580         }
581         else if (is_exactly_a<power>(facmul)
582                  and is_exactly_a<numeric>(facmul.op(1))) {
583                 if (facmul.op(0).is_equal(x))
584                         return a;
585                 size_t expo = ex_to<numeric>(facmul.op(1)).to_int();
586                 for (size_t j=1; j<=expo; ++j) {
587                         ex ee = power(facmul.op(0), numeric(j));
588                         factor.push_back(ee);
589                         cofac.push_back((facmul/ee).expand());
590                 }
591         }
592         else {
593                 return qr.first + qr.second/denom;
594         }
595 	size_t num_factors = factor.size();
596         //std::cerr << "factors  : " << exprseq(factor) << std::endl;
597         //std::cerr << "cofactors: " << exprseq(cofac) << std::endl;
598 
599 	// Construct coefficient matrix for decomposition
600 	int max_denom_deg = ex_to<numeric>(denom.degree(x)).to_int();
601 	matrix sys(max_denom_deg + 1, num_factors);
602 	matrix rhs(max_denom_deg + 1, 1);
603 	for (int i=0; i<=max_denom_deg; i++) {
604 		for (size_t j=0; j<num_factors; j++)
605 			sys(i, j) = cofac[j].coeff(x, i);
606 		rhs(i, 0) = qr.second.coeff(x, i);
607 	}
608 //clog << "coeffs: " << sys << endl;
609 //clog << "rhs   : " << rhs << endl;
610 
611 	// Solve resulting linear system
612 	matrix vars(num_factors, 1);
613 	for (size_t i=0; i<num_factors; i++)
614 		vars(i, 0) = symbol();
615 	matrix sol = sys.solve(vars, rhs);
616 
617 	// Sum up decomposed fractions
618 	ex sum = _ex0;
619 	for (size_t i=0; i<num_factors; i++)
620 		sum += sol(i, 0) / oc / factor[i];
621 
622 	return qr.first + sum;
623 }
624 
625 
626 } // namespace GiNaC
627