Update copyright statements.
[ginac.git] / ginac / polynomial / mgcd.cpp
1 /** @file mgcd.cpp
2  *
3  *  Chinese remainder algorithm. */
4
5 /*
6  *  GiNaC Copyright (C) 1999-2014 Johannes Gutenberg University Mainz, Germany
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation; either version 2 of the License, or
11  *  (at your option) any later version.
12  *
13  *  This program is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with this program; if not, write to the Free Software
20  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
21  */
22
23 #include "operators.h"
24 #include "chinrem_gcd.h"
25 #include "pgcd.h"
26 #include "collect_vargs.h"
27 #include "primes_factory.h"
28 #include "divide_in_z_p.h"
29 #include "poly_cra.h"
30 #include <numeric> // std::accumulate
31
32 #include <cln/integer.h>
33 #include <cln/integer_ring.h>
34 #include <cln/rational.h>
35 #include <cln/rational_ring.h>
36
37 namespace GiNaC {
38
39 static cln::cl_I extract_integer_content(ex& Apr, const ex& A)
40 {
41         static const cln::cl_I n1(1);
42         const numeric icont_ = A.integer_content();
43         GINAC_ASSERT(cln::instanceof(icont_.to_cl_N(), cln::cl_RA_ring));
44         if (cln::instanceof(icont_.to_cl_N(), cln::cl_I_ring)) {
45                 const cln::cl_I icont = cln::the<cln::cl_I>(icont_.to_cl_N());
46                 if (icont != 1) {
47                         Apr = (A/icont_).expand();
48                         return icont;
49                 } else {
50                         Apr = A;
51                         return n1;
52                 }
53         } else {
54                 Apr = (A/icont_).expand();
55                 // A is a polynomail over rationals, so GCD is defined
56                 // up to arbitrary rational number.
57                 return n1;
58         }
59 }
60
61 ex chinrem_gcd(const ex& A_, const ex& B_, const exvector& vars)
62 {
63         ex A, B;
64         const cln::cl_I a_icont = extract_integer_content(A, A_);
65         const cln::cl_I b_icont = extract_integer_content(B, B_);
66         const cln::cl_I c = cln::gcd(a_icont, b_icont);
67
68         const cln::cl_I a_lc = integer_lcoeff(A, vars);
69         const cln::cl_I b_lc = integer_lcoeff(B, vars);
70         const cln::cl_I g_lc = cln::gcd(a_lc, b_lc);
71
72         const ex& x(vars.back());
73         exp_vector_t n = std::min(degree_vector(A, vars), degree_vector(B, vars));
74         const int nTot = std::accumulate(n.begin(), n.end(), 0);
75         const cln::cl_I A_max_coeff = to_cl_I(A.max_coefficient()); 
76         const cln::cl_I B_max_coeff = to_cl_I(B.max_coefficient());
77
78         const cln::cl_I lcoeff_limit = (cln::cl_I(1) << nTot)*cln::abs(g_lc)*
79                 std::min(A_max_coeff, B_max_coeff);
80
81
82         cln::cl_I q = 0;
83         ex H = 0;
84
85         long p;
86         primes_factory pfactory;
87         while (true) {
88                 bool has_primes = pfactory(p, g_lc);
89                 if (!has_primes)
90                         throw chinrem_gcd_failed();
91
92                 const numeric pnum(p);
93                 ex Ap = A.smod(pnum);
94                 ex Bp = B.smod(pnum);
95                 ex Cp = pgcd(Ap, Bp, vars, p);
96
97                 const cln::cl_I g_lcp = smod(g_lc, p); 
98                 const cln::cl_I Cp_lc = integer_lcoeff(Cp, vars);
99                 const cln::cl_I nlc = smod(recip(Cp_lc, p)*g_lcp, p);
100                 Cp = (Cp*numeric(nlc)).expand().smod(pnum);
101                 exp_vector_t cp_deg = degree_vector(Cp, vars);
102                 if (zerop(cp_deg))
103                         return numeric(c);
104                 if (zerop(q)) {
105                         H = Cp;
106                         n = cp_deg;
107                         q = p;
108                 } else {
109                         if (cp_deg == n) {
110                                 ex H_next = chinese_remainder(H, q, Cp, p);
111                                 q = q*cln::cl_I(p);
112                                 H = H_next;
113                         } else if (cp_deg < n) {
114                                 // all previous homomorphisms are unlucky
115                                 q = p;
116                                 H = Cp;
117                                 n = cp_deg;
118                         } else {
119                                 // dp_deg > d_deg: current prime is bad
120                         }
121                 }
122                 if (q < lcoeff_limit)
123                         continue; // don't bother to do division checks
124                 ex C, dummy1, dummy2;
125                 extract_integer_content(C, H);
126                 if (divide_in_z_p(A, C, dummy1, vars, 0) && 
127                                 divide_in_z_p(B, C, dummy2, vars, 0))
128                         return (numeric(c)*C).expand();
129                 // else: try more primes
130         }
131 }
132
133 } // namespace GiNaC