Last active
August 29, 2015 14:26
-
-
Save marionette-of-u/ef42366f82346fdf6496 to your computer and use it in GitHub Desktop.
Symbolic Integration vol.1
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include <iostream> | |
| #include <vector> | |
| #include <functional> | |
| #include <utility> | |
| #include <memory> | |
| #include <stdexcept> | |
| #include <iterator> | |
| #include <algorithm> | |
| #include <limits> | |
| #include <cassert> | |
| #include <ginac/ginac.h> | |
| namespace qz{ | |
| using namespace GiNaC; | |
| inline int factor_class(const ex &f){ | |
| exmap map; | |
| if(f.match(pow(wild(0), wild(1)), map)){ | |
| const ex &g = map[wild(0)]; | |
| if(is_a<symbol>(g)){ | |
| return 7; | |
| } | |
| numeric gp = ex_to<numeric>(g); | |
| if(imag(gp) != 0){ | |
| if(real(gp) != 0){ | |
| return 6; | |
| }else{ | |
| return 5; | |
| } | |
| } | |
| return 4; | |
| }else{ | |
| if(is_a<symbol>(f)){ | |
| return 3; | |
| } | |
| numeric fp = ex_to<numeric>(f); | |
| if(imag(fp) != 0){ | |
| if(real(fp) != 0){ | |
| return 2; | |
| }else{ | |
| return 1; | |
| } | |
| } | |
| return 0; | |
| } | |
| }; | |
| inline int lexicographical_compare(const ex &l, const ex &r){ | |
| if(is_a<add>(l) && is_a<add>(r) || is_a<mul>(l) && is_a<mul>(r)){ | |
| auto iter = l.begin(), jter = r.begin(); | |
| int result; | |
| for(; ; ){ | |
| result = lexicographical_compare(*iter, *jter); | |
| if(result != 0){ | |
| return result; | |
| } | |
| ++iter, ++jter; | |
| if(iter == l.end() && jter != r.end()){ return -1; } | |
| if(iter != l.end() && jter == r.end()){ return +1; } | |
| if(iter == l.end() && jter == r.end()){ return 0; } | |
| } | |
| return result; | |
| } | |
| if(!is_a<mul>(l) && is_a<mul>(r) || is_a<add>(l) && !is_a<add>(r)){ | |
| auto iter = l.begin(), jter = r.begin(); | |
| std::size_t n = 0; | |
| int result; | |
| for(; iter != l.end(); ++iter, ++n){ | |
| for(; jter != r.end(); ++jter){ | |
| result = lexicographical_compare(*iter, *jter); | |
| if(result != 0){ return result; } | |
| } | |
| } | |
| return n == 1 ? -1 : +1; | |
| } | |
| if(is_a<mul>(l) && !is_a<mul>(r) || !is_a<add>(l) && is_a<add>(r)){ | |
| auto iter = l.begin(), jter = r.begin(); | |
| std::size_t n = 0; | |
| int result; | |
| for(; jter != r.end(); ++jter, ++n){ | |
| for(; iter != l.end(); ++iter){ | |
| result = lexicographical_compare(*iter, *jter); | |
| if(result != 0){ return result; } | |
| } | |
| } | |
| return n == 1 ? +1 : -1; | |
| } | |
| auto primitive_compare = [](auto &&a, auto &&b){ | |
| return a < b ? -1 : a > b ? +1 : 0; | |
| }; | |
| if(l == 0 && r == 0){ return 0; } | |
| if(l == 0 && r != 0){ return -1; } | |
| if(l != 0 && r ==0 ){ return +1; } | |
| int l_class = factor_class(l), r_class = factor_class(r); | |
| if(l_class < r_class){ | |
| return -1; | |
| }else if(l_class > r_class){ | |
| return +1; | |
| } | |
| int result = 0; | |
| switch(l_class){ | |
| case 7: | |
| { | |
| exmap lmap, rmap; | |
| l.match(pow(wild(0), wild(1)), lmap); | |
| symbol fp = ex_to<symbol>(lmap[wild(0)]); | |
| r.match(pow(wild(0), wild(1)), rmap); | |
| symbol gp = ex_to<symbol>(rmap[wild(0)]); | |
| result = primitive_compare(fp.get_name(), gp.get_name()); | |
| if(result == 0){ result = lexicographical_compare(lmap[wild(1)], rmap[wild(1)]); } | |
| } | |
| break; | |
| case 6: | |
| { | |
| exmap lmap, rmap; | |
| l.match(pow(wild(0), wild(1)), lmap); | |
| numeric fp = ex_to<numeric>(lmap[wild(0)]); | |
| r.match(pow(wild(0), wild(1)), rmap); | |
| numeric gp = ex_to<numeric>(rmap[wild(0)]); | |
| result = primitive_compare(real(fp), real(gp)); | |
| if(result == 0){ result = primitive_compare(imag(fp), imag(gp)); } | |
| if(result == 0){ result = lexicographical_compare(lmap[wild(1)], rmap[wild(1)]); } | |
| } | |
| break; | |
| case 5: | |
| { | |
| exmap lmap, rmap; | |
| l.match(pow(wild(0), wild(1)), lmap); | |
| numeric fp = ex_to<numeric>(lmap[wild(0)]); | |
| r.match(pow(wild(0), wild(1)), rmap); | |
| numeric gp = ex_to<numeric>(rmap[wild(0)]); | |
| result = primitive_compare(imag(fp), imag(gp)); | |
| if(result == 0){ result = lexicographical_compare(lmap[wild(1)], rmap[wild(1)]); } | |
| } | |
| break; | |
| case 4: | |
| { | |
| exmap lmap, rmap; | |
| l.match(pow(wild(0), wild(1)), lmap); | |
| numeric fp = ex_to<numeric>(lmap[wild(0)]); | |
| r.match(pow(wild(0), wild(1)), rmap); | |
| numeric gp = ex_to<numeric>(rmap[wild(0)]); | |
| result = primitive_compare(real(fp), real(gp)); | |
| if(result == 0){ result = lexicographical_compare(lmap[wild(1)], rmap[wild(1)]); } | |
| } | |
| break; | |
| case 3: | |
| { | |
| symbol fp = ex_to<symbol>(l), gp = ex_to<symbol>(r); | |
| result = primitive_compare(ex_to<symbol>(l).get_name(), ex_to<symbol>(r).get_name()); | |
| } | |
| break; | |
| case 2: | |
| { | |
| numeric f = ex_to<numeric>(l), g = ex_to<numeric>(r); | |
| result = primitive_compare(real(f), real(g)); | |
| if(result == 0){ result = primitive_compare(imag(f), imag(g)); } | |
| } | |
| break; | |
| case 1: | |
| { | |
| numeric f = ex_to<numeric>(l), g = ex_to<numeric>(r); | |
| result = primitive_compare(imag(f), imag(g)); | |
| } | |
| break; | |
| case 0: | |
| { | |
| numeric f = ex_to<numeric>(l), g = ex_to<numeric>(r); | |
| result = primitive_compare(real(f), real(g)); | |
| } | |
| break; | |
| } | |
| return result; | |
| } | |
| inline const ex lt(const ex &a){ | |
| if(is_a<add>(a)){ | |
| return *a.begin(); | |
| }else{ | |
| return a; | |
| } | |
| } | |
| inline std::tuple<ex, ex> poly_divide(const ex &A, const ex &B, const ex &x){ | |
| std::tuple<ex, ex> QR; | |
| ex &Q = std::get<0>(QR), &R = std::get<1>(QR); | |
| R = A.expand(); | |
| for(; ; ){ | |
| if(R == 0){ break; } | |
| int delta = R.degree(x) - B.degree(x); | |
| if(delta < 0){ break; } | |
| ex T = (R.lcoeff(x) / B.lcoeff(x) * pow(x, delta)).expand(); | |
| Q = (Q + T).expand(); | |
| R = (R - B * T).expand(); | |
| } | |
| return QR; | |
| } | |
| inline std::tuple<ex, ex> poly_pseudo_divide(const ex &A, const ex &B, const ex &x){ | |
| ex b = B.lcoeff(x); | |
| int N = A.degree(x) - B.degree(x) + 1; | |
| ex Q = 0, R = A.expand(); | |
| for(; ; ){ | |
| if(R == 0){ break; } | |
| int delta = R.degree(x) - B.degree(x); | |
| if(delta < 0){ break; } | |
| ex T = R.lcoeff(x) * pow(x, delta); | |
| --N; | |
| Q = (b * Q + T).expand(); | |
| R = (b * R - T * B).expand(); | |
| } | |
| return std::make_tuple((pow(b, N) * Q).expand(), (pow(b, N) * R).expand()); | |
| } | |
| inline std::tuple<ex, ex> multivar_poly_divide(const ex &f, const ex &g){ | |
| auto check_exp = [](const ex &a){ | |
| if(is_a<mul>(a)){ | |
| for(auto iter = a.begin(); iter != a.end(); ++iter){ | |
| exmap map; | |
| if(iter->match(pow(wild(0), wild(1)), map)){ | |
| if(map[wild(1)] < 0){ | |
| return false; | |
| } | |
| } | |
| } | |
| return true; | |
| }else{ | |
| return true; | |
| } | |
| }; | |
| ex r = 0, p = f, q = 0; | |
| ex ltf = lt(f); | |
| while(p != 0){ | |
| ex ltp = lt(p), ltg = lt(g); | |
| ex h = (ltp / ltg).expand(); | |
| if(check_exp(h)){ | |
| q = (q + h).expand(); | |
| p = (p - h * g).expand(); | |
| }else{ | |
| r = (r + ltp).expand(); | |
| p = (p - ltp).expand(); | |
| } | |
| } | |
| return std::make_tuple(q, r); | |
| } | |
| inline ex euclidean( | |
| const ex &A, | |
| const ex &B, | |
| const ex &x, | |
| std::function<std::tuple<ex, ex>(const ex&, const ex&, const ex&)> euclidean_division = poly_pseudo_divide | |
| ){ | |
| ex a = A, b = B; | |
| while(b != 0){ | |
| auto qr = euclidean_division(a, b, x); | |
| a = b; | |
| b = std::get<1>(qr); | |
| } | |
| return a; | |
| } | |
| inline ex multivar_euclidean(const ex &A, const ex &B){ | |
| ex a = A, b = B; | |
| while(b != 0){ | |
| auto qr = multivar_poly_divide(a, b); | |
| a = b; | |
| b = std::get<1>(qr); | |
| } | |
| return a; | |
| } | |
| inline std::tuple<ex, ex, ex> extended_euclidean( | |
| const ex &A, | |
| const ex &B, | |
| const ex &x, | |
| std::function<std::tuple<ex, ex>(const ex&, const ex&, const ex&)> euclidean_division = poly_pseudo_divide | |
| ){ | |
| ex a = A, b = B, a1 = 1, a2 = 0, b1 = 0, b2 = 1; | |
| while(b != 0){ | |
| auto qr = euclidean_division(a, b, x); | |
| a = b, b = std::get<1>(qr); | |
| ex r1 = (a1 - std::get<0>(qr) * b1).expand(), r2 = (a2 - std::get<0>(qr) * b2).expand(); | |
| a1 = b1, a2 = b2, b1 = r1, b2 = r2; | |
| } | |
| return std::make_tuple(a1, a2, a); | |
| } | |
| inline std::tuple<ex, ex> half_extended_euclidean( | |
| const ex &A, | |
| const ex &B, | |
| const ex &x, | |
| std::function<std::tuple<ex, ex>(const ex&, const ex&, const ex&)> euclidean_division = poly_divide | |
| ){ | |
| ex a = A, b = B, a1 = 1, b1 = 0; | |
| while(b != 0){ | |
| auto qr = euclidean_division(a, b, x); | |
| a = b, b = std::get<1>(qr); | |
| ex r1 = a1 - std::get<0>(qr) * b1; | |
| a1 = b1, b1 = r1.expand(); | |
| } | |
| return std::make_tuple(a1, a); | |
| } | |
| inline std::tuple<ex, ex, ex> extended_euclidean_half_full( | |
| const ex &a, | |
| const ex &b, | |
| const ex &x, | |
| std::function<std::tuple<ex, ex>(const ex&, const ex&, const ex&)> euclidean_division = poly_divide | |
| ){ | |
| auto sg = half_extended_euclidean(a, b, x, euclidean_division); | |
| auto tr = euclidean_division(std::get<1>(sg) - std::get<0>(sg) * a, b, x); | |
| return std::make_tuple(std::get<0>(sg), std::get<0>(tr), std::get<1>(sg)); | |
| } | |
| inline std::tuple<ex, ex> extended_euclidean( | |
| const ex &a, | |
| const ex &b, | |
| const ex &c, | |
| const ex &x, | |
| std::function<std::tuple<ex, ex>(const ex&, const ex&, const ex&)> euclidean_division = poly_divide | |
| ){ | |
| auto stg = extended_euclidean(a, b, x, euclidean_division); | |
| ex &s = std::get<0>(stg), &t = std::get<1>(stg), &g = std::get<2>(stg); | |
| auto qr = euclidean_division(c, g, x); | |
| ex &q = std::get<0>(qr), &r = std::get<1>(qr); | |
| if(r != 0){ | |
| throw std::runtime_error("c is not in the ideal generated by a and b."); | |
| } | |
| s = (q * s).expand(), t = (q * t).expand(); | |
| if(s != 0 && s.degree(x) >= b.degree(x)){ | |
| auto qr1 = euclidean_division(s, b, x); | |
| q = std::get<0>(qr1), r = std::get<1>(qr1); | |
| s = r, t = (t + q * a).expand(); | |
| } | |
| return std::make_tuple(s, t); | |
| } | |
| inline ex half_extended_euclidean(const ex &a, const ex &b, const ex &c, const ex &x){ | |
| auto sg = half_extended_euclidean(a, b, x); | |
| ex &s = std::get<0>(sg), &g = std::get<1>(sg); | |
| auto qr = poly_divide(c, g, x); | |
| ex &q = std::get<0>(qr), &r = std::get<1>(qr); | |
| if(r != 0){ | |
| throw std::runtime_error("c is not in the ideal generated by a and b."); | |
| } | |
| s = (q * s).expand(); | |
| if(s != 0 && s.degree(x) >= b.degree(x)){ | |
| auto qr1 = poly_divide(s, b, x); | |
| s = std::get<1>(qr1); | |
| } | |
| return s; | |
| } | |
| inline std::tuple<ex, ex> extended_euclidean_half_full(const ex &a, const ex &b, const ex &c, const ex &x){ | |
| ex s = half_extended_euclidean(a, b, c, x); | |
| auto tr = poly_divide(c - s * a, b, x); | |
| return std::make_tuple(s, std::get<0>(tr)); | |
| } | |
| inline std::vector<ex> partial_fraction_impl( | |
| const ex &a, | |
| std::vector<ex>::const_iterator begin, | |
| std::vector<ex>::const_iterator end, | |
| const ex &x | |
| ){ | |
| ex d = 1; | |
| for(auto iter = begin; iter != end; ++iter){ | |
| d = d * *iter; | |
| } | |
| d = d.expand(); | |
| auto a0_r = poly_divide(a, d, x); | |
| ex &a0 = std::get<0>(a0_r), &r = std::get<1>(a0_r); | |
| if(std::distance(begin, end) == 1){ | |
| std::vector<ex> v = { a0, r }; | |
| return v; | |
| } | |
| ex d2_dn = 1; | |
| for(auto iter = begin + 1; iter != end; ++iter){ | |
| d2_dn = (d2_dn * *iter).expand(); | |
| } | |
| auto a1_t = extended_euclidean_half_full(d2_dn, *begin, r, x); | |
| ex &a1 = std::get<0>(a1_t), &t = std::get<1>(a1_t); | |
| std::vector<ex> b0_a2_an = partial_fraction_impl(t, begin + 1, end, x); | |
| std::vector<ex> result; | |
| result.reserve(b0_a2_an.size() + 1); | |
| result.push_back((a0 + b0_a2_an.front()).expand()); | |
| result.push_back(std::move(a1)); | |
| for(auto iter = b0_a2_an.begin() + 1; iter != b0_a2_an.end(); ++iter){ | |
| result.push_back(*iter); | |
| } | |
| return result; | |
| } | |
| inline std::vector<ex> partial_fraction( | |
| const ex &a, | |
| const std::vector<ex> &v, | |
| const ex &x | |
| ){ | |
| return partial_fraction_impl(a, v.begin(), v.end(), x); | |
| } | |
| inline std::vector<ex> complete_partial_fraction( | |
| const ex &a, | |
| const std::vector<std::pair<ex, int>> &de, | |
| const ex &x | |
| ){ | |
| std::vector<ex> pf_d; | |
| pf_d.reserve(de.size()); | |
| for(auto &p : de){ | |
| pf_d.push_back(pow(p.first, p.second).expand()); | |
| } | |
| std::vector<ex> an = partial_fraction(a, pf_d, x); | |
| std::vector<std::vector<ex>> anm; | |
| anm.resize(an.size() - 1); | |
| for(std::size_t i = 1; i < an.size(); ++i){ | |
| for(std::size_t j = de[i - 1].second; ; ){ | |
| auto qa = poly_divide(an[i], de[i - 1].first, x); | |
| ex &q = std::get<0>(qa), &a = std::get<1>(qa); | |
| anm[i - 1].push_back(a); | |
| an[i] = q; | |
| if(j == 1){ break; } | |
| --j; | |
| } | |
| an[0] = (an[0] + an[i]).expand(); | |
| } | |
| std::vector<ex> result; | |
| std::size_t s = 1; | |
| for(auto &i : anm){ s += i.size(); } | |
| result.reserve(s); | |
| result.push_back(an[0]); | |
| for(auto &i : anm){ | |
| for(auto &j : i){ | |
| result.push_back(j); | |
| } | |
| } | |
| return result; | |
| } | |
| inline std::pair<ex, std::vector<ex>> subresultant(const ex &A, const ex &B, const ex &x){ | |
| std::vector<ex> R; | |
| R.push_back(A); | |
| R.push_back(B); | |
| ex gamma = -1; | |
| std::vector<ex> Delta = { A.degree(x) - B.degree(x) }; | |
| std::vector<ex> Beta = { (A.degree(x) - B.degree(x) + 1) & 1 == 1 ? -1 : +1 }; | |
| ex *beta = &Beta.back(); | |
| std::vector<ex> r_vec; | |
| while(!R.back().is_zero()){ | |
| r_vec.push_back(R.back().lcoeff(x)); | |
| std::tuple<ex, ex> qr = poly_pseudo_divide(R[R.size() - 2], R[R.size() - 1], x); | |
| ex &rem = std::get<1>(qr); | |
| R.push_back(rem / *beta); | |
| gamma = (pow(-r_vec.back(), Delta.back()) * pow(gamma, 1 - Delta.back())).expand(); | |
| Delta.push_back(R[R.size() - 2].degree(x) - R[R.size() - 1].degree(x)); | |
| Beta.push_back((-r_vec.back() * pow(gamma, Delta.back())).expand()); | |
| beta = &Beta.back(); | |
| } | |
| std::size_t k = R.size() - 2; | |
| if(R[k].degree(x) > 0){ | |
| return std::make_pair(0, std::move(R)); | |
| } | |
| if(R[k - 1].degree(x) == 1){ | |
| return std::make_pair(R[k], std::move(R)); | |
| } | |
| int s = 1; | |
| ex c = 1; | |
| for(std::size_t j = 1; j < k - 1; ++j){ | |
| if(R[j - 1].degree(x) & 1 == 1 && R[j].degree(x) & 1 == 1){ | |
| s = -s; | |
| } | |
| c = (c * pow((Beta[j - 1] / pow(r_vec[j - 1], 1 + Delta[j - 1])), R[j].degree(x)) * pow(r_vec[j - 1], R[j - 1].degree(x) - R[j + 1].degree(x)).expand()).expand(); | |
| } | |
| return std::make_pair((s * c * pow(R[k], R[k - 1].degree(x))).expand(), std::move(R)); | |
| } | |
| template<class Lambda> | |
| inline std::vector<std::pair<int, ex>> decomp_factor(const Lambda &lambda){ | |
| std::vector<std::pair<int, ex>> result; | |
| ex f = lambda(); | |
| if(is_a<mul>(f)){ | |
| for(auto iter = f.begin(); iter != f.end(); ++iter){ | |
| exmap map; | |
| if(iter->match(pow(wild(0), wild(1)), map)){ | |
| result.push_back(std::make_pair(ex_to<numeric>(map[wild(1)]).to_int(), map[wild(0)])); | |
| }else{ | |
| result.push_back(std::make_pair(1, *iter)); | |
| } | |
| } | |
| }else{ | |
| exmap map; | |
| if(f.match(pow(wild(0), wild(1)), map)){ | |
| result.push_back(std::make_pair(ex_to<numeric>(map[wild(1)]).to_int(), map[wild(0)])); | |
| }else{ | |
| result.push_back(std::make_pair(1, f)); | |
| } | |
| } | |
| return result; | |
| } | |
| inline std::vector<std::pair<int, ex>> factor_vec(const ex &x){ | |
| return decomp_factor([&]() -> ex{ return factor(x); }); | |
| } | |
| inline ex content(const ex &A, const symbol &x){ | |
| if(is_a<add>(A)){ | |
| auto iter = A.begin(), jter = iter; ++jter; | |
| ex f = gcd(iter->lcoeff(x), jter->lcoeff(x), nullptr, nullptr); | |
| for(; jter != A.end(); ++jter){ | |
| f = gcd(f, jter->lcoeff(x)); | |
| } | |
| return f; | |
| }else{ | |
| return A.begin()->lcoeff(x); | |
| } | |
| } | |
| inline std::vector<std::pair<int, ex>> squarefree(const ex &a, const symbol &x){ | |
| std::vector<std::pair<int, ex>> res; | |
| ex w = a; | |
| ex z = w.diff(x); | |
| ex g = gcd(w, z); | |
| if(g.is_equal(a)){ | |
| res.push_back(std::make_pair(1, a)); | |
| return res; | |
| } | |
| ex y; | |
| int j = 1; | |
| do{ | |
| w = quo(w, g, x); | |
| y = quo(z, g, x); | |
| z = y - w.diff(x); | |
| g = gcd(w, z); | |
| res.push_back(std::make_pair(j++, g)); | |
| }while(!z.is_zero()); | |
| return res; | |
| } | |
| inline std::tuple<ex, ex> hermite_reduce(const ex &A_, const ex &D, const symbol &x){ | |
| ex A = A_; | |
| ex g = 0, D_minus = gcd(D, D.diff(x), nullptr, nullptr).expand(), D_star = quo(D, D_minus, x); | |
| while(D_minus.degree(x) > 0){ | |
| ex D_minus2 = gcd(D_minus, D_minus.diff(x), nullptr, nullptr).expand(); | |
| ex D_minus_star = quo(D_minus, D_minus2, x); | |
| auto BC = extended_euclidean(quo((-D_star * D_minus.diff(x)).expand(), D_minus, x), D_minus_star, A, x); | |
| ex &B = std::get<0>(BC), &C = std::get<1>(BC); | |
| A = (C - (quo((B.diff(x) * D_star).expand(), D_minus_star, x))).expand(); | |
| g = (g + (B / D_minus)).expand(); | |
| D_minus = D_minus2; | |
| } | |
| return std::make_tuple(g, A / D_star); | |
| } | |
| inline std::vector<numeric> horner(const std::vector<numeric> &a, std::size_t n, const numeric &x0){ | |
| std::vector<numeric> b; | |
| b = a; | |
| for(std::size_t i = 0; i <= n; ++i){ | |
| for(std::size_t j = 1; j <= n - i; ++j){ | |
| b[j] += x0 * b[j - 1]; | |
| } | |
| } | |
| return b; | |
| } | |
| inline std::vector<numeric> aberth( | |
| const ex &e, | |
| const std::vector<numeric> &coeff, | |
| const numeric &max_coeff, | |
| const symbol &x, | |
| const numeric &epsilon = 1.0 / 1024.0 | |
| ){ | |
| std::vector<numeric> z; | |
| std::size_t n = e.degree(x); | |
| z.resize(n); | |
| numeric zc = -coeff[1] / (coeff[0] * n); | |
| std::vector<numeric> a = horner(coeff, n, zc); | |
| ex b, db; | |
| b += abs(a[0]); | |
| for(std::size_t i = 1; i <= n; ++i){ | |
| b += -abs(a[i]) * pow(x, i); | |
| } | |
| db = b.diff(x); | |
| numeric xn = 100.0, xm; | |
| for(; ; ){ | |
| xm = xn - ex_to<numeric>(b.subs(x == xn) / db.subs(x == xn)); | |
| if(abs(xn - xm) <= epsilon){ | |
| xn = xm; | |
| break; | |
| } | |
| xn = xm; | |
| } | |
| numeric pi = ex_to<numeric>(Pi.evalf()); | |
| for(std::size_t j = 0; j < n; ++j){ | |
| numeric theta = (2 * pi / n) * j + pi / (2 * n); | |
| z[j] = zc + cos(theta) + I * sin(theta); | |
| } | |
| return z; | |
| } | |
| inline std::vector<ex> nth_roots(const ex &poly, const symbol &x, const numeric epsilon = 1.0 / 1024.0){ | |
| numeric max_coeff = 0; | |
| std::vector<numeric> coeff; | |
| std::size_t n = poly.degree(x); | |
| if(n == 1){ | |
| std::vector<ex> result({ (-poly.coeff(x, 0) / poly.coeff(x, 1)).expand() }); | |
| return result; | |
| } | |
| if(n == 2){ | |
| ex D = pow(pow(poly.coeff(x, 1), 2) - 4 * poly.coeff(x, 2) * poly.coeff(x, 0), ex(1) / ex(2)); | |
| std::vector<ex> result({ | |
| ((-poly.coeff(x, 1) + D) / (2 * poly.coeff(x, 2))).expand(), | |
| ((-poly.coeff(x, 1) - D) / (2 * poly.coeff(x, 2))).expand() | |
| }); | |
| return result; | |
| } | |
| if(n == 3){ | |
| ex A = poly.coeff(x, 3), B = poly.coeff(x, 2), C = poly.coeff(x, 1), D = poly.coeff(x, 0); | |
| ex b = B / A, c = C/ A, d = D / A; | |
| ex p = c - pow(b, 2) / 3, q = d - b * c / 3 + 2 * pow(b, 3) / 27; | |
| symbol t; | |
| ex T = pow(t, 2) + q * t - pow(p, 3) / 27; | |
| std::vector<ex> sol_t = nth_roots(T.expand(), t); | |
| ex u = pow(sol_t[0], ex(1) / ex(3)), v = pow(sol_t[1], ex(1) / ex(3)), w = exp(2 * Pi * I / 3); | |
| std::vector<ex> result({ | |
| (u + v - b / 3).expand(), | |
| (u * w + v * pow(w, 2) - b / 3).expand(), | |
| (u * pow(w, 2) + v * w - b / 3).expand(), | |
| }); | |
| return result; | |
| } | |
| if(n == 4){ | |
| auto cbrt = [](ex a, int n){ | |
| ex Pi2 = 2 * Pi; | |
| ex rho = pow(abs(a), ex(1) / ex(3)); | |
| ex theta = ((Pi2 * n) + atan2(imag_part(a), real_part(a))) / 3; | |
| return (rho * cos(theta) + rho * sin(theta) * I).expand(); | |
| }; | |
| ex a = poly.coeff(x, 4), b = poly.coeff(x, 3), c = poly.coeff(x, 2), d = poly.coeff(x, 1), e = poly.coeff(x, 0); | |
| b /= a; | |
| c /= a; | |
| d /= a; | |
| e /= a; | |
| ex b2 = b * b; | |
| ex b3 = b * b2; | |
| ex b4 = b2 * b2; | |
| ex alpha = -3 * b2 / 8 + c; | |
| ex beta = (b3 / 8 - b * c / 2 + d).expand(); | |
| ex gamma = -3 *b4 / 256 + b2 * c / 16 - b * d / 4 + e; | |
| ex alpha2 = alpha * alpha; | |
| ex t = -b / 4; | |
| if(beta.is_zero()){ | |
| ex rad = pow(alpha2, ex(1) / ex(2)); | |
| ex r1 = pow((-alpha + rad) / 2, ex(1) / ex(2)); | |
| ex r2 = pow((-alpha - rad) / 2, ex(1) / ex(2)); | |
| std::vector<ex> result({ | |
| (t + r1).expand(), | |
| (t - r1).expand(), | |
| (t + r2).expand(), | |
| (t - r2).expand() | |
| }); | |
| return result; | |
| }else{ | |
| ex alpha3 = alpha * alpha2; | |
| ex P = -(alpha2 / 12 + gamma); | |
| ex Q = -alpha3 / 108 + alpha * gamma / 3 - beta * beta / 8; | |
| ex R = -Q / 2 + pow(pow(Q, 2) / 4 + pow(P, 3) / 27, ex(1) / ex(2)); | |
| ex U = cbrt(R, 0); | |
| ex y = -5 * alpha / 6 + U; | |
| if(U.is_zero()){ | |
| y -= cbrt(Q, 0); | |
| }else{ | |
| y -= P / (3 * U); | |
| } | |
| ex W = pow(alpha + 2 * y, ex(1) / ex(2)); | |
| ex r1 = pow(-(3 * alpha + 2 * y + 2 * beta / W), ex(1) / ex(2)); | |
| ex r2 = pow(-(3 * alpha + 2 * y - 2 * beta / W), ex(1) / ex(2)); | |
| std::vector<ex> result({ | |
| (t + (+W + r1) / 2).expand(), | |
| (t + (+W - r1) / 2).expand(), | |
| (t + (-W + r2) / 2).expand(), | |
| (t + (-W - r2) / 2).expand() | |
| }); | |
| return result; | |
| } | |
| } | |
| coeff.resize(n + 1); | |
| for(std::size_t i = 0; i <= n; ++i){ | |
| coeff[i] = ex_to<numeric>(poly.coeff(x, i)); | |
| if(max_coeff < abs(coeff[i])){ | |
| max_coeff = abs(coeff[i]); | |
| } | |
| } | |
| std::vector<numeric> z = aberth(poly, coeff, max_coeff, x, epsilon), zp; | |
| for(; ; ){ | |
| zp = z; | |
| for(std::size_t j = 0; j < n; ++j){ | |
| numeric f = ex_to<numeric>(poly.subs(x == z[j])); | |
| numeric df = coeff[0]; | |
| for(std::size_t i = 0; i < n; ++i){ | |
| if(i != j){ | |
| df *= zp[j] - zp[i]; | |
| } | |
| } | |
| z[j] = zp[j] - f / df; | |
| } | |
| numeric err = 0; | |
| for(std::size_t j = 0; j < n; ++j){ | |
| numeric err_prime = abs(ex_to<numeric>(poly.subs(x == z[j]))); | |
| if(err < err_prime){ | |
| err = err_prime; | |
| } | |
| } | |
| if(err < epsilon){ | |
| break; | |
| } | |
| } | |
| std::vector<ex> ze(z.size()); | |
| for(auto &a : z){ | |
| ze.push_back(a); | |
| } | |
| return ze; | |
| } | |
| inline ex int_rational_log_part(const ex &A, const ex &D, const symbol &x){ | |
| std::vector<std::pair<ex, ex>> QS; | |
| ex sum; | |
| symbol t; | |
| std::pair<ex, std::vector<ex>> R = subresultant(D, (A - t * D.diff(x)).expand(), x); | |
| std::vector<std::pair<int, ex>> Q = squarefree(R.first, t); | |
| for(const std::pair<int, ex> &Qi : Q){ | |
| int i = Qi.first; | |
| const ex &q = Qi.second; | |
| if(q.degree(t) <= 0){ | |
| continue; | |
| } | |
| if(i == D.degree(x)){ | |
| std::vector<ex> roots = nth_roots(q, t); | |
| for(auto &a : roots){ | |
| sum += a * log(D.subs(t == a)); | |
| } | |
| }else{ | |
| ex Si = [&]() -> const ex&{ | |
| for(std::size_t m = 1; m < R.second.size() - 1; ++m){ | |
| if(R.second[m].degree(x) == i){ | |
| return R.second[m]; | |
| } | |
| } | |
| throw; | |
| }(); | |
| std::vector<std::pair<int, ex>> Aq = squarefree(Si.lcoeff(x), t); | |
| for(const std::pair<int, ex> &Aj : Aq){ | |
| int j = Aj.first; | |
| const ex &a = Aj.second; | |
| Si = Si / pow(gcd(a, q, nullptr, nullptr), j); | |
| } | |
| std::vector<ex> roots = nth_roots(q, t); | |
| for(auto &a : roots){ | |
| sum += a * log(Si.subs(t == a)); | |
| } | |
| } | |
| ++i; | |
| } | |
| return sum; | |
| } | |
| class p_integral_s{ | |
| public: | |
| ex expression; | |
| symbol x; | |
| inline p_integral_s(){} | |
| inline p_integral_s(const ex &expression, const symbol &x) : expression(expression), x(x){} | |
| inline p_integral_s(const p_integral_s &other) : expression(other.expression), x(other.x){} | |
| ~p_integral_s() = default; | |
| }; | |
| inline bool operator ==(const p_integral_s &lhs, const p_integral_s &rhs){ | |
| return lhs.expression == rhs.expression && lhs.x == rhs.x; | |
| } | |
| inline bool operator <(const p_integral_s &lhs, const p_integral_s &rhs){ | |
| int comp = lhs.x.compare(rhs.x); | |
| if(comp != 0){ | |
| return comp; | |
| }else{ | |
| return lhs.expression.compare(rhs.expression); | |
| } | |
| } | |
| } | |
| namespace GiNaC{ | |
| using p_integral = structure<qz::p_integral_s, compare_std_less>; | |
| template<> | |
| inline void p_integral::print(const print_context &c, unsigned level) const{ | |
| if(is_a<print_tree>(c)){ | |
| inherited::print(c, level); | |
| } | |
| const qz::p_integral_s &p_i = get_struct(); | |
| c.s << "∫(" << p_i.expression << ")d" << p_i.x; | |
| } | |
| template<> | |
| inline std::size_t p_integral::nops() const{ | |
| return 1; | |
| } | |
| template<> | |
| inline ex p_integral::op(std::size_t i) const{ | |
| switch(i){ | |
| case 0: | |
| return get_struct().expression; | |
| default: | |
| throw std::range_error("p_integral::op(): no such operand"); | |
| } | |
| } | |
| template<> | |
| inline ex &p_integral::let_op(std::size_t i){ | |
| ensure_if_modifiable(); | |
| switch(i){ | |
| case 0: | |
| return get_struct().expression; | |
| default: | |
| throw std::range_error("p_integral::let_op(): no such operand"); | |
| } | |
| } | |
| template<> | |
| inline const char *p_integral::get_class_name(){ return "p_integral"; } | |
| } | |
| namespace qz{ | |
| inline ex p_integ(const ex &expression, const symbol &x){ | |
| if(expression.is_zero()){ | |
| return 0; | |
| } | |
| return p_integral(p_integral_s(expression, x)); | |
| } | |
| inline ex integrate_rational_function(const ex &f, const symbol &x){ | |
| std::tuple<ex, ex> gh = hermite_reduce(f.numer(), f.denom(), x); | |
| std::tuple<ex, ex> QR = poly_divide(std::get<1>(gh).numer(), std::get<1>(gh).denom(), x); | |
| if(std::get<1>(QR).is_zero()){ | |
| return std::get<0>(gh) + p_integ(std::get<0>(QR), x); | |
| }else{ | |
| return std::get<0>(gh) + p_integ(std::get<0>(QR), x) + int_rational_log_part(std::get<1>(QR), std::get<1>(gh).denom(), x); | |
| } | |
| } | |
| } | |
| int main(){ | |
| using namespace GiNaC; | |
| using namespace qz; | |
| symbol t("t"), u("u"), v("v"), x("x"), y("y"), z("z"); | |
| ex f = ex(36) / (pow(x, 5) - 2 * pow(x, 4) - 2 * pow(x, 3) + 4 * pow(x, 2) + x - 2); | |
| std::cout << integrate_rational_function(f, x).subs(x == Pi).evalf() << std::endl; | |
| return 0; | |
| } | |
| void lexicographical_compare_test(){ | |
| using namespace GiNaC; | |
| using namespace qz; | |
| symbol t("t"), u("u"), v("v"), x("x"), y("y"), z("z"); | |
| { | |
| ex e = t; | |
| ex f = t; | |
| std::cout << e << ", " << f << " : " << lexicographical_compare(e, f) << std::endl; | |
| assert(lexicographical_compare(e, f) == 0); | |
| } | |
| { | |
| ex e = t; | |
| ex f = u; | |
| std::cout << e << ", " << f << " : " << lexicographical_compare(e, f) << std::endl; | |
| assert(lexicographical_compare(e, f) == -1); | |
| } | |
| { | |
| ex e = u; | |
| ex f = t; | |
| std::cout << e << ", " << f << " : " << lexicographical_compare(e, f) << std::endl; | |
| assert(lexicographical_compare(e, f) == +1); | |
| } | |
| { | |
| ex e = 1; | |
| ex f = 1; | |
| std::cout << e << ", " << f << " : " << lexicographical_compare(e, f) << std::endl; | |
| assert(lexicographical_compare(e, f) == 0); | |
| } | |
| { | |
| ex e = 2; | |
| ex f = 1; | |
| std::cout << e << ", " << f << " : " << lexicographical_compare(e, f) << std::endl; | |
| assert(lexicographical_compare(e, f) == +1); | |
| } | |
| { | |
| ex e = 1; | |
| ex f = 2; | |
| std::cout << e << ", " << f << " : " << lexicographical_compare(e, f) << std::endl; | |
| assert(lexicographical_compare(e, f) == -1); | |
| } | |
| { | |
| ex e = 1 + I; | |
| ex f = 1 + I; | |
| std::cout << e << ", " << f << " : " << lexicographical_compare(e, f) << std::endl; | |
| assert(lexicographical_compare(e, f) == 0); | |
| } | |
| { | |
| ex e = 2 + I; | |
| ex f = 1 + I; | |
| std::cout << e << ", " << f << " : " << lexicographical_compare(e, f) << std::endl; | |
| assert(lexicographical_compare(e, f) == +1); | |
| } | |
| { | |
| ex e = 1 + I; | |
| ex f = 2 + I; | |
| std::cout << e << ", " << f << " : " << lexicographical_compare(e, f) << std::endl; | |
| assert(lexicographical_compare(e, f) == -1); | |
| } | |
| { | |
| ex e = 1 + 2 * I; | |
| ex f = 1 + I; | |
| std::cout << e << ", " << f << " : " << lexicographical_compare(e, f) << std::endl; | |
| assert(lexicographical_compare(e, f) == +1); | |
| } | |
| { | |
| ex e = 1 + I; | |
| ex f = 1 + 2 * I; | |
| std::cout << e << ", " << f << " : " << lexicographical_compare(e, f) << std::endl; | |
| assert(lexicographical_compare(e, f) == -1); | |
| } | |
| { | |
| ex e = pow(x, 2); | |
| ex f = pow(x, 2); | |
| std::cout << e << ", " << f << " : " << lexicographical_compare(e, f) << std::endl; | |
| assert(lexicographical_compare(e, f) == 0); | |
| } | |
| { | |
| ex e = pow(x, 2); | |
| ex f = pow(y, 2); | |
| std::cout << e << ", " << f << " : " << lexicographical_compare(e, f) << std::endl; | |
| assert(lexicographical_compare(e, f) == -1); | |
| } | |
| { | |
| ex e = pow(y, 2); | |
| ex f = pow(x, 2); | |
| std::cout << e << ", " << f << " : " << lexicographical_compare(e, f) << std::endl; | |
| assert(lexicographical_compare(e, f) == +1); | |
| } | |
| { | |
| ex e = pow(x, 3); | |
| ex f = pow(x, 2); | |
| std::cout << e << ", " << f << " : " << lexicographical_compare(e, f) << std::endl; | |
| assert(lexicographical_compare(e, f) == +1); | |
| } | |
| { | |
| ex e = pow(x, 2); | |
| ex f = pow(x, 3); | |
| std::cout << e << ", " << f << " : " << lexicographical_compare(e, f) << std::endl; | |
| assert(lexicographical_compare(e, f) == -1); | |
| } | |
| { | |
| ex e = pow(x, 3); | |
| ex f = pow(y, 2); | |
| std::cout << e << ", " << f << " : " << lexicographical_compare(e, f) << std::endl; | |
| assert(lexicographical_compare(e, f) == -1); | |
| } | |
| { | |
| ex e = pow(x, 2); | |
| ex f = pow(y, 3); | |
| std::cout << e << ", " << f << " : " << lexicographical_compare(e, f) << std::endl; | |
| assert(lexicographical_compare(e, f) == -1); | |
| } | |
| { | |
| ex e = pow(y, 3); | |
| ex f = pow(x, 2); | |
| std::cout << e << ", " << f << " : " << lexicographical_compare(e, f) << std::endl; | |
| assert(lexicographical_compare(e, f) == +1); | |
| } | |
| { | |
| ex e = pow(y, 2); | |
| ex f = pow(x, 3); | |
| std::cout << e << ", " << f << " : " << lexicographical_compare(e, f) << std::endl; | |
| assert(lexicographical_compare(e, f) == +1); | |
| } | |
| { | |
| ex e = pow(x, pow(x, 2)); | |
| ex f = pow(x, pow(x, 2)); | |
| std::cout << e << ", " << f << " : " << lexicographical_compare(e, f) << std::endl; | |
| assert(lexicographical_compare(e, f) == 0); | |
| } | |
| { | |
| ex e = pow(x, pow(x, 2)); | |
| ex f = pow(x, pow(y, 2)); | |
| std::cout << e << ", " << f << " : " << lexicographical_compare(e, f) << std::endl; | |
| assert(lexicographical_compare(e, f) == -1); | |
| } | |
| { | |
| ex e = pow(x, pow(y, 2)); | |
| ex f = pow(x, pow(x, 2)); | |
| std::cout << e << ", " << f << " : " << lexicographical_compare(e, f) << std::endl; | |
| assert(lexicographical_compare(e, f) == +1); | |
| } | |
| { | |
| ex e =pow(x, pow(x, 3)); | |
| ex f = pow(x, pow(x, 2)); | |
| std::cout << e << ", " << f << " : " << lexicographical_compare(e, f) << std::endl; | |
| assert(lexicographical_compare(e, f) == +1); | |
| } | |
| { | |
| ex e = pow(x, pow(x, 2)); | |
| ex f = pow(x, pow(x, 3)); | |
| std::cout << e << ", " << f << " : " << lexicographical_compare(e, f) << std::endl; | |
| assert(lexicographical_compare(e, f) == -1); | |
| } | |
| { | |
| ex e = pow(x, pow(x, 3)); | |
| ex f = pow(x, pow(y, 2)); | |
| std::cout << e << ", " << f << " : " << lexicographical_compare(e, f) << std::endl; | |
| assert(lexicographical_compare(e, f) == -1); | |
| } | |
| { | |
| ex e = pow(x, pow(x, 2)); | |
| ex f = pow(x, pow(y, 3)); | |
| std::cout << e << ", " << f << " : " << lexicographical_compare(e, f) << std::endl; | |
| assert(lexicographical_compare(e, f) == -1); | |
| } | |
| { | |
| ex e = pow(x, pow(y, 3)); | |
| ex f = pow(x, pow(x, 2)); | |
| std::cout << e << ", " << f << " : " << lexicographical_compare(e, f) << std::endl; | |
| assert(lexicographical_compare(e, f) == +1); | |
| } | |
| { | |
| ex e = pow(x, pow(y, 2)); | |
| ex f = pow(x, pow(x, 3)); | |
| std::cout << e << ", " << f << " : " << lexicographical_compare(e, f) << std::endl; | |
| assert(lexicographical_compare(e, f) == +1); | |
| } | |
| { | |
| ex e = pow(x, 1 + I); | |
| ex f = pow(x, 1 + I); | |
| std::cout << e << ", " << f << " : " << lexicographical_compare(e, f) << std::endl; | |
| assert(lexicographical_compare(e, f) == 0); | |
| } | |
| { | |
| ex e = pow(x, 1 + I); | |
| ex f = pow(y, 1 + I); | |
| std::cout << e << ", " << f << " : " << lexicographical_compare(e, f) << std::endl; | |
| assert(lexicographical_compare(e, f) == -1); | |
| } | |
| { | |
| ex e = pow(x, 1 + 2 * I); | |
| ex f = pow(x, 1 + I); | |
| std::cout << e << ", " << f << " : " << lexicographical_compare(e, f) << std::endl; | |
| assert(lexicographical_compare(e, f) == +1); | |
| } | |
| { | |
| ex e = pow(x, 1 + I); | |
| ex f = pow(x, 1 + 2 * I); | |
| std::cout << e << ", " << f << " : " << lexicographical_compare(e, f) << std::endl; | |
| assert(lexicographical_compare(e, f) == -1); | |
| } | |
| { | |
| ex e = pow(x, 1 + I) + pow(y, 1 + I); | |
| ex f = pow(x, 1 + I) + pow(y, 1 + 2 * I); | |
| std::cout << e << ", " << f << " : " << lexicographical_compare(e, f) << std::endl; | |
| assert(lexicographical_compare(e, f) == -1); | |
| } | |
| { | |
| ex e = pow(y, 1 + I) * pow(x, 1 + I); | |
| ex f = pow(y, 1 + I) * pow(x, 1 + 2 * I); | |
| std::cout << e << ", " << f << " : " << lexicographical_compare(e, f) << std::endl; | |
| assert(lexicographical_compare(e, f) == -1); | |
| } | |
| { | |
| ex e = pow(y, 1 + I) + pow(x, 1 + I); | |
| ex f = pow(y, 1 + I) * pow(x, 1 + 2 * I); | |
| std::cout << e << ", " << f << " : " << lexicographical_compare(e, f) << std::endl; | |
| assert(lexicographical_compare(e, f) == -1); | |
| } | |
| { | |
| ex e = pow(x, 1 + I); | |
| ex f = pow(y, 1 + I) * pow(x, 1 + 2 * I); | |
| std::cout << e << ", " << f << " : " << lexicographical_compare(e, f) << std::endl; | |
| assert(lexicographical_compare(e, f) == -1); | |
| } | |
| { | |
| ex e = pow(x, 1 + I); | |
| ex f = pow(y, 1 + I) + pow(x, 1 + 2 * I); | |
| std::cout << e << ", " << f << " : " << lexicographical_compare(e, f) << std::endl; | |
| assert(lexicographical_compare(e, f) == -1); | |
| } | |
| { | |
| ex e = pow(x, 3 + I); | |
| ex f = pow(y, 1 + I) + pow(x, 2); | |
| std::cout << e << ", " << f << " : " << lexicographical_compare(e, f) << std::endl; | |
| assert(lexicographical_compare(e, f) == -1); | |
| } | |
| } | |
| // g++ -std=c++14 main.cpp -o test -O3 -lcln -lginac |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment