1 /* mpz_powm_ui(res,base,exp,mod) -- Set R to (B^E) mod M.
2 
3    Contributed to the GNU project by Torbjörn Granlund.
4 
5 Copyright 1991, 1993, 1994, 1996, 1997, 2000-2002, 2005, 2008, 2009,
6 2011-2013, 2015 Free Software Foundation, Inc.
7 
8 This file is part of the GNU MP Library.
9 
10 The GNU MP Library is free software; you can redistribute it and/or modify
11 it under the terms of either:
12 
13   * the GNU Lesser General Public License as published by the Free
14     Software Foundation; either version 3 of the License, or (at your
15     option) any later version.
16 
17 or
18 
19   * the GNU General Public License as published by the Free Software
20     Foundation; either version 2 of the License, or (at your option) any
21     later version.
22 
23 or both in parallel, as here.
24 
25 The GNU MP Library is distributed in the hope that it will be useful, but
26 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
27 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
28 for more details.
29 
30 You should have received copies of the GNU General Public License and the
31 GNU Lesser General Public License along with the GNU MP Library.  If not,
32 see https://www.gnu.org/licenses/.  */
33 
34 
35 #include "gmp-impl.h"
36 #include "longlong.h"
37 
38 
39 /* This code is very old, and should be rewritten to current GMP standard.  It
40    is slower than mpz_powm for large exponents, but also for small exponents
41    when the mod argument is small.
42 
43    As an intermediate solution, we now deflect to mpz_powm for exponents >= 20.
44 */
45 
46 /*
47   b ^ e mod m   res
48   0   0     0    ?
49   0   e     0    ?
50   0   0     m    ?
51   0   e     m    0
52   b   0     0    ?
53   b   e     0    ?
54   b   0     m    1 mod m
55   b   e     m    b^e mod m
56 */
57 
58 static void
mod(mp_ptr np,mp_size_t nn,mp_srcptr dp,mp_size_t dn,gmp_pi1_t * dinv,mp_ptr tp)59 mod (mp_ptr np, mp_size_t nn, mp_srcptr dp, mp_size_t dn, gmp_pi1_t *dinv, mp_ptr tp)
60 {
61   mp_ptr qp = tp;
62 
63   if (dn == 1)
64     {
65       np[0] = mpn_divrem_1 (qp, (mp_size_t) 0, np, nn, dp[0]);
66     }
67   else if (dn == 2)
68     {
69       mpn_div_qr_2n_pi1 (qp, np, np, nn, dp[1], dp[0], dinv->inv32);
70     }
71   else if (BELOW_THRESHOLD (dn, DC_DIV_QR_THRESHOLD) ||
72              BELOW_THRESHOLD (nn - dn, DC_DIV_QR_THRESHOLD))
73     {
74       mpn_sbpi1_div_qr (qp, np, nn, dp, dn, dinv->inv32);
75     }
76   else if (BELOW_THRESHOLD (dn, MUPI_DIV_QR_THRESHOLD) ||   /* fast condition */
77              BELOW_THRESHOLD (nn, 2 * MU_DIV_QR_THRESHOLD) || /* fast condition */
78              (double) (2 * (MU_DIV_QR_THRESHOLD - MUPI_DIV_QR_THRESHOLD)) * dn /* slow... */
79              + (double) MUPI_DIV_QR_THRESHOLD * nn > (double) dn * nn)    /* ...condition */
80     {
81       mpn_dcpi1_div_qr (qp, np, nn, dp, dn, dinv);
82     }
83   else
84     {
85       /* We need to allocate separate remainder area, since mpn_mu_div_qr does
86            not handle overlap between the numerator and remainder areas.
87            FIXME: Make it handle such overlap.  */
88       mp_ptr rp, scratch;
89       mp_size_t itch;
90       TMP_DECL;
91       TMP_MARK;
92 
93       itch = mpn_mu_div_qr_itch (nn, dn, 0);
94       rp = TMP_BALLOC_LIMBS (dn);
95       scratch = TMP_BALLOC_LIMBS (itch);
96 
97       mpn_mu_div_qr (qp, rp, np, nn, dp, dn, scratch);
98       MPN_COPY (np, rp, dn);
99 
100       TMP_FREE;
101     }
102 }
103 
104 /* Compute t = a mod m, a is defined by (ap,an), m is defined by (mp,mn), and
105    t is defined by (tp,mn).  */
106 static void
reduce(mp_ptr tp,mp_srcptr ap,mp_size_t an,mp_srcptr mp,mp_size_t mn,gmp_pi1_t * dinv)107 reduce (mp_ptr tp, mp_srcptr ap, mp_size_t an, mp_srcptr mp, mp_size_t mn, gmp_pi1_t *dinv)
108 {
109   mp_ptr rp, scratch;
110   TMP_DECL;
111   TMP_MARK;
112 
113   TMP_ALLOC_LIMBS_2 (rp, an, scratch, an - mn + 1);
114   MPN_COPY (rp, ap, an);
115   mod (rp, an, mp, mn, dinv, scratch);
116   MPN_COPY (tp, rp, mn);
117 
118   TMP_FREE;
119 }
120 
121 void
mpz_powm_ui(mpz_ptr r,mpz_srcptr b,unsigned long int el,mpz_srcptr m)122 mpz_powm_ui (mpz_ptr r, mpz_srcptr b, unsigned long int el, mpz_srcptr m)
123 {
124   if (el < 20)
125     {
126       mp_ptr xp, tp, mp, bp, scratch;
127       mp_size_t xn, tn, mn, bn;
128       int m_zero_cnt;
129       int c;
130       mp_limb_t e, m2;
131       gmp_pi1_t dinv;
132       TMP_DECL;
133 
134       mp = PTR(m);
135       mn = ABSIZ(m);
136       if (UNLIKELY (mn == 0))
137           DIVIDE_BY_ZERO;
138 
139       if (el <= 1)
140           {
141             if (el == 1)
142               {
143                 mpz_mod (r, b, m);
144                 return;
145               }
146             /* Exponent is zero, result is 1 mod M, i.e., 1 or 0 depending on if
147                M equals 1.  */
148             SIZ(r) = mn != 1 || mp[0] != 1;
149             MPZ_NEWALLOC (r, 1)[0] = 1;
150             return;
151           }
152 
153       TMP_MARK;
154 
155       /* Normalize m (i.e. make its most significant bit set) as required by
156            division functions below.  */
157       count_leading_zeros (m_zero_cnt, mp[mn - 1]);
158       m_zero_cnt -= GMP_NAIL_BITS;
159       if (m_zero_cnt != 0)
160           {
161             mp_ptr new_mp = TMP_ALLOC_LIMBS (mn);
162             mpn_lshift (new_mp, mp, mn, m_zero_cnt);
163             mp = new_mp;
164           }
165 
166       m2 = mn == 1 ? 0 : mp[mn - 2];
167       invert_pi1 (dinv, mp[mn - 1], m2);
168 
169       bn = ABSIZ(b);
170       bp = PTR(b);
171       if (bn > mn)
172           {
173             /* Reduce possibly huge base.  Use a function call to reduce, since we
174                don't want the quotient allocation to live until function return.  */
175             mp_ptr new_bp = TMP_ALLOC_LIMBS (mn);
176             reduce (new_bp, bp, bn, mp, mn, &dinv);
177             bp = new_bp;
178             bn = mn;
179             /* Canonicalize the base, since we are potentially going to multiply with
180                it quite a few times.  */
181             MPN_NORMALIZE (bp, bn);
182           }
183 
184       if (bn == 0)
185           {
186             SIZ(r) = 0;
187             TMP_FREE;
188             return;
189           }
190 
191       TMP_ALLOC_LIMBS_3 (xp, mn, scratch, mn + 1, tp, 2 * mn + 1);
192 
193       MPN_COPY (xp, bp, bn);
194       xn = bn;
195 
196       e = el;
197       count_leading_zeros (c, e);
198       e = (e << c) << 1;                /* shift the exp bits to the left, lose msb */
199       c = GMP_LIMB_BITS - 1 - c;
200 
201       ASSERT (c != 0); /* el > 1 */
202           {
203             /* Main loop. */
204             do
205               {
206                 mpn_sqr (tp, xp, xn);
207                 tn = 2 * xn; tn -= tp[tn - 1] == 0;
208                 if (tn < mn)
209                     {
210                       MPN_COPY (xp, tp, tn);
211                       xn = tn;
212                     }
213                 else
214                     {
215                       mod (tp, tn, mp, mn, &dinv, scratch);
216                       MPN_COPY (xp, tp, mn);
217                       xn = mn;
218                     }
219 
220                 if ((mp_limb_signed_t) e < 0)
221                     {
222                       mpn_mul (tp, xp, xn, bp, bn);
223                       tn = xn + bn; tn -= tp[tn - 1] == 0;
224                       if (tn < mn)
225                         {
226                           MPN_COPY (xp, tp, tn);
227                           xn = tn;
228                         }
229                       else
230                         {
231                           mod (tp, tn, mp, mn, &dinv, scratch);
232                           MPN_COPY (xp, tp, mn);
233                           xn = mn;
234                         }
235                     }
236                 e <<= 1;
237                 c--;
238               }
239             while (c != 0);
240           }
241 
242       /* We shifted m left m_zero_cnt steps.  Adjust the result by reducing it
243            with the original M.  */
244       if (m_zero_cnt != 0)
245           {
246             mp_limb_t cy;
247             cy = mpn_lshift (tp, xp, xn, m_zero_cnt);
248             tp[xn] = cy; xn += cy != 0;
249 
250             if (xn >= mn)
251               {
252                 mod (tp, xn, mp, mn, &dinv, scratch);
253                 xn = mn;
254               }
255             mpn_rshift (xp, tp, xn, m_zero_cnt);
256           }
257       MPN_NORMALIZE (xp, xn);
258 
259       if ((el & 1) != 0 && SIZ(b) < 0 && xn != 0)
260           {
261             mp = PTR(m);                          /* want original, unnormalized m */
262             mpn_sub (xp, mp, mn, xp, xn);
263             xn = mn;
264             MPN_NORMALIZE (xp, xn);
265           }
266       MPZ_NEWALLOC (r, xn);
267       SIZ (r) = xn;
268       MPN_COPY (PTR(r), xp, xn);
269 
270       TMP_FREE;
271     }
272   else
273     {
274       /* For large exponents, fake an mpz_t exponent and deflect to the more
275            sophisticated mpz_powm.  */
276       mpz_t e;
277       mp_limb_t ep[LIMBS_PER_ULONG];
278       MPZ_FAKE_UI (e, ep, el);
279       mpz_powm (r, b, e, m);
280     }
281 }
282