From e5fedf6b3da7dc11b558628e1280cb252eafde04 Mon Sep 17 00:00:00 2001 From: Richard Kreckel Date: Sun, 18 Sep 2022 21:36:31 +0200 Subject: [PATCH] Make sqrfree_parfrac() work in the general case and make it supported. --- check/exam_sqrfree.cpp | 41 ++++++++++++++++++++++++++- doc/tutorial/ginac.texi | 21 ++++++++++++++ ginac/normal.cpp | 62 +++++++++++++++++++++++++++++------------ ginsh/ginsh.1.in | 3 ++ ginsh/ginsh_parser.ypp | 6 ++++ 5 files changed, 114 insertions(+), 19 deletions(-) diff --git a/check/exam_sqrfree.cpp b/check/exam_sqrfree.cpp index 79d776f6..3e94ed67 100644 --- a/check/exam_sqrfree.cpp +++ b/check/exam_sqrfree.cpp @@ -197,7 +197,46 @@ unsigned exam_sqrfree() return result; } +unsigned exam_sqrfree_parfrac() +{ + symbol x("x"); + // (expression, number of terms after partial fraction decomposition) + vector> exams = { + {ex("(x - 1) / (x*(x^2 + 2))", lst{x}), 2}, + {ex("(1 - x^10) / x", lst{x}), 2}, + {ex("(2*x^3 + x + 3) / ((x^2 + 1)^2)", lst{x}), 2}, + {ex("1 / (x * (x+1) * (x+2))", lst{x}), 3}, + {ex("(x*x + 3*x - 1) / (x^2*(x^2 + 2)^2)", lst{x}), 4}, + {ex("(1 - x^10) / (x + 2)", lst{x}), 11}, + {ex("(1 - x + 3*x^2) / (x^3 * (2+x)^2)", lst{x}), 5}, + {ex("(1 - x) / (x^4 * (x - 2)^3)", lst{x}), 6}, + {ex("(1 - 2*x + x^9) / (x^5 * (1 - x + x^2)^5)", lst{x}), 12} + }; + unsigned result = 0; + + cout << "\n" + << "examining square-free partial fraction decomposition" << flush; + for (auto e: exams) { + ex e1 = e.first; + ex e2 = sqrfree_parfrac(e1, x); + if (e2.nops() != e.second && + !normal(e1-e2).is_zero()) { + clog << "sqrfree_parfrac(" << e1 << ", " << x << ") erroneously returned " + << e2 << endl; + ++result; + } + cout << '.' << flush; + } + + return result; +} + int main(int argc, char** argv) { - return exam_sqrfree(); + unsigned result = 0; + + result += exam_sqrfree(); + result += exam_sqrfree_parfrac(); + + return result; } diff --git a/doc/tutorial/ginac.texi b/doc/tutorial/ginac.texi index c840fb69..40368aef 100644 --- a/doc/tutorial/ginac.texi +++ b/doc/tutorial/ginac.texi @@ -5379,6 +5379,27 @@ some care with subsequent processing of the result: Note also, how factors with the same exponents are not fully factorized with this method. +@subsection Square-free partial fraction decomposition +@cindex square-free partial fraction decomposition +@cindex partial fraction decomposition +@cindex @code{sqrfree_parfrac()} + +GiNaC also supports square-free partial fraction decomposition of +rational functions: +@example +ex sqrfree_parfrac(const ex & a, const symbol & x); +@end example +It is called square-free because it assumes a square-free +factorization of the input's denominator: +@example + ... + symbol x("x"); + + ex rat = (x-4)/(pow(x,2)*(x+2)); + cout << sqrfree_parfrac(rat, x) << endl; + // -> -2*x^(-2)+3/2*x^(-1)-3/2*(2+x)^(-1) +@end example + @subsection Polynomial factorization @cindex factorization @cindex polynomial factorization diff --git a/ginac/normal.cpp b/ginac/normal.cpp index 383115f0..cad9d455 100644 --- a/ginac/normal.cpp +++ b/ginac/normal.cpp @@ -1958,10 +1958,12 @@ ex sqrfree_parfrac(const ex & a, const symbol & x) // Factorize denominator and compute cofactors epvector yun = sqrfree_yun(denom, x); exvector factor, cofac; + int dim = 0; for (size_t i=0; i(yun[i].coeff); for (size_t j=0; j(yun[i].coeff)); + for (size_t j=0; j(yun[i].coeff)); + for (size_t j=0; j(e[1])); } +static ex f_sqrfree_parfrac(const exprseq &e) +{ + return sqrfree_parfrac(e[0], ex_to(e[1])); +} + static ex f_subs3(const exprseq &e) { CHECK_ARG(1, lst, subs); @@ -747,6 +752,7 @@ static const fcn_init builtin_fcns[] = { {"sprem", f_sprem, 3}, {"sqrfree", f_sqrfree1, 1}, {"sqrfree", f_sqrfree2, 2}, + {"sqrfree_parfrac", f_sqrfree_parfrac, 2}, {"sqrt", f_sqrt, 1}, {"subs", f_subs2, 2}, {"subs", f_subs3, 3}, -- 2.49.0