initially checkin
[gnupg.git] / mpi / mpih-mul.c
1 /* mpihelp-mul.c  -  MPI helper functions
2  *      Copyright (c) 1997 by Werner Koch (dd9jn)
3  *
4  * This file is part of G10.
5  *
6  * G10 is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * G10 is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19  */
20
21 #include <config.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include "mpi-internal.h"
25 #include "longlong.h"
26
27 /* If KARATSUBA_THRESHOLD is not already defined, define it to a
28  * value which is good on most machines.  */
29 #ifndef KARATSUBA_THRESHOLD
30     #define KARATSUBA_THRESHOLD 32
31 #endif
32
33 /* The code can't handle KARATSUBA_THRESHOLD smaller than 2.  */
34 #if KARATSUBA_THRESHOLD < 2
35     #undef KARATSUBA_THRESHOLD
36     #define KARATSUBA_THRESHOLD 2
37 #endif
38
39
40 #define MPN_MUL_N_RECURSE(prodp, up, vp, size, tspace) \
41     do {                                                \
42         if( (size) < KARATSUBA_THRESHOLD )              \
43             mul_n_basecase (prodp, up, vp, size);       \
44         else                                            \
45             mul_n (prodp, up, vp, size, tspace);        \
46     } while (0);
47
48 #define MPN_SQR_N_RECURSE(prodp, up, size, tspace) \
49     do {                                            \
50         if ((size) < KARATSUBA_THRESHOLD)           \
51             sqr_n_basecase (prodp, up, size);       \
52         else                                        \
53             sqr_n (prodp, up, size, tspace);        \
54     } while (0);
55
56
57
58 mpi_limb_t
59 mpihelp_addmul_1( mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr,
60                   mpi_size_t s1_size, mpi_limb_t s2_limb)
61 {
62     mpi_limb_t cy_limb;
63     mpi_size_t j;
64     mpi_limb_t prod_high, prod_low;
65     mpi_limb_t x;
66
67     /* The loop counter and index J goes from -SIZE to -1.  This way
68      * the loop becomes faster.  */
69     j = -s1_size;
70     res_ptr -= j;
71     s1_ptr -= j;
72
73     cy_limb = 0;
74     do {
75         umul_ppmm( prod_high, prod_low, s1_ptr[j], s2_limb );
76
77         prod_low += cy_limb;
78         cy_limb = (prod_low < cy_limb?1:0) + prod_high;
79
80         x = res_ptr[j];
81         prod_low = x + prod_low;
82         cy_limb += prod_low < x?1:0;
83         res_ptr[j] = prod_low;
84     } while ( ++j );
85     return cy_limb;
86 }
87
88
89 mpi_limb_t
90 mpihelp_submul_1( mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr,
91                   mpi_size_t s1_size, mpi_limb_t s2_limb)
92 {
93     mpi_limb_t cy_limb;
94     mpi_size_t j;
95     mpi_limb_t prod_high, prod_low;
96     mpi_limb_t x;
97
98     /* The loop counter and index J goes from -SIZE to -1.  This way
99      * the loop becomes faster.  */
100     j = -s1_size;
101     res_ptr -= j;
102     s1_ptr -= j;
103
104     cy_limb = 0;
105     do {
106         umul_ppmm( prod_high, prod_low, s1_ptr[j], s2_limb);
107
108         prod_low += cy_limb;
109         cy_limb = (prod_low < cy_limb?1:0) + prod_high;
110
111         x = res_ptr[j];
112         prod_low = x - prod_low;
113         cy_limb += prod_low > x?1:0;
114         res_ptr[j] = prod_low;
115     } while( ++j );
116
117     return cy_limb;
118 }
119
120 mpi_limb_t
121 mpihelp_mul_1( mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr, mpi_size_t s1_size,
122                                                     mpi_limb_t s2_limb)
123 {
124     mpi_limb_t cy_limb;
125     mpi_size_t j;
126     mpi_limb_t prod_high, prod_low;
127
128     /* The loop counter and index J goes from -S1_SIZE to -1.  This way
129      * the loop becomes faster.  */
130     j = -s1_size;
131
132     /* Offset the base pointers to compensate for the negative indices.  */
133     s1_ptr -= j;
134     res_ptr -= j;
135
136     cy_limb = 0;
137     do {
138         umul_ppmm( prod_high, prod_low, s1_ptr[j], s2_limb );
139         prod_low += cy_limb;
140         cy_limb = (prod_low < cy_limb?1:0) + prod_high;
141         res_ptr[j] = prod_low;
142     } while( ++j );
143
144     return cy_limb;
145 }
146
147
148 /* Multiply the natural numbers u (pointed to by UP) and v (pointed to by VP),
149  * both with SIZE limbs, and store the result at PRODP.  2 * SIZE limbs are
150  * always stored.  Return the most significant limb.
151  *
152  * Argument constraints:
153  * 1. PRODP != UP and PRODP != VP, i.e. the destination
154  *    must be distinct from the multiplier and the multiplicand.
155  *
156  *
157  * Handle simple cases with traditional multiplication.
158  *
159  * This is the most critical code of multiplication.  All multiplies rely
160  * on this, both small and huge.  Small ones arrive here immediately.  Huge
161  * ones arrive here as this is the base case for Karatsuba's recursive
162  * algorithm below.
163  */
164
165 static mpi_limb_t
166 mul_n_basecase( mpi_ptr_t prodp, mpi_ptr_t up,
167                                  mpi_ptr_t vp, mpi_size_t size)
168 {
169     mpi_size_t i;
170     mpi_limb_t cy;
171     mpi_limb_t v_limb;
172
173     /* Multiply by the first limb in V separately, as the result can be
174      * stored (not added) to PROD.  We also avoid a loop for zeroing.  */
175     v_limb = vp[0];
176     if( v_limb <= 1 ) {
177         if( v_limb == 1 )
178             MPN_COPY( prodp, up, size );
179         else
180             MPN_ZERO( prodp, size );
181         cy = 0;
182     }
183     else
184         cy = mpihelp_mul_1( prodp, up, size, v_limb );
185
186     prodp[size] = cy;
187     prodp++;
188
189     /* For each iteration in the outer loop, multiply one limb from
190      * U with one limb from V, and add it to PROD.  */
191     for( i = 1; i < size; i++ ) {
192         v_limb = vp[i];
193         if( v_limb <= 1 ) {
194             cy = 0;
195             if( v_limb == 1 )
196                cy = mpihelp_add_n(prodp, prodp, up, size);
197         }
198         else
199             cy = mpihelp_addmul_1(prodp, up, size, v_limb);
200
201         prodp[size] = cy;
202         prodp++;
203     }
204
205     return cy;
206 }
207
208
209 static void
210 mul_n( mpi_ptr_t prodp, mpi_ptr_t up, mpi_ptr_t vp,
211                         mpi_size_t size, mpi_ptr_t tspace )
212 {
213     if( size & 1 ) {
214       /* The size is odd, the code code below doesn't handle that.
215        * Multiply the least significant (size - 1) limbs with a recursive
216        * call, and handle the most significant limb of S1 and S2
217        * separately.
218        * A slightly faster way to do this would be to make the Karatsuba
219        * code below behave as if the size were even, and let it check for
220        * odd size in the end.  I.e., in essence move this code to the end.
221        * Doing so would save us a recursive call, and potentially make the
222        * stack grow a lot less.
223        */
224       mpi_size_t esize = size - 1;       /* even size */
225       mpi_limb_t cy_limb;
226
227       MPN_MUL_N_RECURSE( prodp, up, vp, esize, tspace );
228       cy_limb = mpihelp_addmul_1( prodp + esize, up, esize, vp[esize] );
229       prodp[esize + esize] = cy_limb;
230       cy_limb = mpihelp_addmul_1( prodp + esize, vp, size, up[esize] );
231       prodp[esize + size] = cy_limb;
232     }
233     else {
234         /* Anatolij Alekseevich Karatsuba's divide-and-conquer algorithm.
235          *
236          * Split U in two pieces, U1 and U0, such that
237          * U = U0 + U1*(B**n),
238          * and V in V1 and V0, such that
239          * V = V0 + V1*(B**n).
240          *
241          * UV is then computed recursively using the identity
242          *
243          *        2n   n          n                     n
244          * UV = (B  + B )U V  +  B (U -U )(V -V )  +  (B + 1)U V
245          *                1 1        1  0   0  1              0 0
246          *
247          * Where B = 2**BITS_PER_MP_LIMB.
248          */
249         mpi_size_t hsize = size >> 1;
250         mpi_limb_t cy;
251         int negflg;
252
253         /* Product H.      ________________  ________________
254          *                |_____U1 x V1____||____U0 x V0_____|
255          * Put result in upper part of PROD and pass low part of TSPACE
256          * as new TSPACE.
257          */
258         MPN_MUL_N_RECURSE(prodp + size, up + hsize, vp + hsize, hsize, tspace);
259
260         /* Product M.      ________________
261          *                |_(U1-U0)(V0-V1)_|
262          */
263         if( mpihelp_cmp(up + hsize, up, hsize) >= 0 ) {
264             mpihelp_sub_n(prodp, up + hsize, up, hsize);
265             negflg = 0;
266         }
267         else {
268             mpihelp_sub_n(prodp, up, up + hsize, hsize);
269             negflg = 1;
270         }
271         if( mpihelp_cmp(vp + hsize, vp, hsize) >= 0 ) {
272             mpihelp_sub_n(prodp + hsize, vp + hsize, vp, hsize);
273             negflg ^= 1;
274         }
275         else {
276             mpihelp_sub_n(prodp + hsize, vp, vp + hsize, hsize);
277             /* No change of NEGFLG.  */
278         }
279         /* Read temporary operands from low part of PROD.
280          * Put result in low part of TSPACE using upper part of TSPACE
281          * as new TSPACE.
282          */
283         MPN_MUL_N_RECURSE(tspace, prodp, prodp + hsize, hsize, tspace + size);
284
285         /* Add/copy product H. */
286         MPN_COPY (prodp + hsize, prodp + size, hsize);
287         cy = mpihelp_add_n( prodp + size, prodp + size,
288                             prodp + size + hsize, hsize);
289
290         /* Add product M (if NEGFLG M is a negative number) */
291         if(negflg)
292             cy -= mpihelp_sub_n(prodp + hsize, prodp + hsize, tspace, size);
293         else
294             cy += mpihelp_add_n(prodp + hsize, prodp + hsize, tspace, size);
295
296         /* Product L.      ________________  ________________
297          *                |________________||____U0 x V0_____|
298          * Read temporary operands from low part of PROD.
299          * Put result in low part of TSPACE using upper part of TSPACE
300          * as new TSPACE.
301          */
302         MPN_MUL_N_RECURSE(tspace, up, vp, hsize, tspace + size);
303
304         /* Add/copy Product L (twice) */
305
306         cy += mpihelp_add_n(prodp + hsize, prodp + hsize, tspace, size);
307         if( cy )
308           mpihelp_add_1(prodp + hsize + size, prodp + hsize + size, hsize, cy);
309
310         MPN_COPY(prodp, tspace, hsize);
311         cy = mpihelp_add_n(prodp + hsize, prodp + hsize, tspace + hsize, hsize);
312         if( cy )
313             mpihelp_add_1(prodp + size, prodp + size, size, 1);
314     }
315 }
316
317
318 static void
319 sqr_n_basecase( mpi_ptr_t prodp, mpi_ptr_t up, mpi_size_t size )
320 {
321     mpi_size_t i;
322     mpi_limb_t cy_limb;
323     mpi_limb_t v_limb;
324
325     /* Multiply by the first limb in V separately, as the result can be
326      * stored (not added) to PROD.  We also avoid a loop for zeroing.  */
327     v_limb = up[0];
328     if( v_limb <= 1 ) {
329         if( v_limb == 1 )
330             MPN_COPY( prodp, up, size );
331         else
332             MPN_ZERO(prodp, size);
333         cy_limb = 0;
334     }
335     else
336         cy_limb = mpihelp_mul_1( prodp, up, size, v_limb );
337
338     prodp[size] = cy_limb;
339     prodp++;
340
341     /* For each iteration in the outer loop, multiply one limb from
342      * U with one limb from V, and add it to PROD.  */
343     for( i=1; i < size; i++) {
344         v_limb = up[i];
345         if( v_limb <= 1 ) {
346             cy_limb = 0;
347             if( v_limb == 1 )
348                 cy_limb = mpihelp_add_n(prodp, prodp, up, size);
349         }
350         else
351             cy_limb = mpihelp_addmul_1(prodp, up, size, v_limb);
352
353         prodp[size] = cy_limb;
354         prodp++;
355     }
356 }
357
358
359 static void
360 sqr_n( mpi_ptr_t prodp, mpi_ptr_t up, mpi_size_t size, mpi_ptr_t tspace)
361 {
362     if( size & 1 ) {
363         /* The size is odd, the code code below doesn't handle that.
364          * Multiply the least significant (size - 1) limbs with a recursive
365          * call, and handle the most significant limb of S1 and S2
366          * separately.
367          * A slightly faster way to do this would be to make the Karatsuba
368          * code below behave as if the size were even, and let it check for
369          * odd size in the end.  I.e., in essence move this code to the end.
370          * Doing so would save us a recursive call, and potentially make the
371          * stack grow a lot less.
372          */
373         mpi_size_t esize = size - 1;       /* even size */
374         mpi_limb_t cy_limb;
375
376         MPN_SQR_N_RECURSE( prodp, up, esize, tspace );
377         cy_limb = mpihelp_addmul_1( prodp + esize, up, esize, up[esize] );
378         prodp[esize + esize] = cy_limb;
379         cy_limb = mpihelp_addmul_1( prodp + esize, up, size, up[esize] );
380
381         prodp[esize + size] = cy_limb;
382     }
383     else {
384         mpi_size_t hsize = size >> 1;
385         mpi_limb_t cy;
386
387         /* Product H.      ________________  ________________
388          *                |_____U1 x U1____||____U0 x U0_____|
389          * Put result in upper part of PROD and pass low part of TSPACE
390          * as new TSPACE.
391          */
392         MPN_SQR_N_RECURSE(prodp + size, up + hsize, hsize, tspace);
393
394         /* Product M.      ________________
395          *                |_(U1-U0)(U0-U1)_|
396          */
397         if( mpihelp_cmp( up + hsize, up, hsize) >= 0 )
398             mpihelp_sub_n( prodp, up + hsize, up, hsize);
399         else
400             mpihelp_sub_n (prodp, up, up + hsize, hsize);
401
402         /* Read temporary operands from low part of PROD.
403          * Put result in low part of TSPACE using upper part of TSPACE
404          * as new TSPACE.  */
405         MPN_SQR_N_RECURSE(tspace, prodp, hsize, tspace + size);
406
407         /* Add/copy product H  */
408         MPN_COPY(prodp + hsize, prodp + size, hsize);
409         cy = mpihelp_add_n(prodp + size, prodp + size,
410                            prodp + size + hsize, hsize);
411
412         /* Add product M (if NEGFLG M is a negative number).  */
413         cy -= mpihelp_sub_n (prodp + hsize, prodp + hsize, tspace, size);
414
415         /* Product L.      ________________  ________________
416          *                |________________||____U0 x U0_____|
417          * Read temporary operands from low part of PROD.
418          * Put result in low part of TSPACE using upper part of TSPACE
419          * as new TSPACE.  */
420         MPN_SQR_N_RECURSE (tspace, up, hsize, tspace + size);
421
422         /* Add/copy Product L (twice).  */
423         cy += mpihelp_add_n (prodp + hsize, prodp + hsize, tspace, size);
424         if( cy )
425             mpihelp_add_1(prodp + hsize + size, prodp + hsize + size,
426                                                             hsize, cy);
427
428         MPN_COPY(prodp, tspace, hsize);
429         cy = mpihelp_add_n (prodp + hsize, prodp + hsize, tspace + hsize, hsize);
430         if( cy )
431             mpihelp_add_1 (prodp + size, prodp + size, size, 1);
432     }
433 }
434
435
436 /* This should be made into an inline function in gmp.h.  */
437 void
438 mpihelp_mul_n( mpi_ptr_t prodp, mpi_ptr_t up, mpi_ptr_t vp, mpi_size_t size)
439 {
440     if( up == vp ) {
441         if( size < KARATSUBA_THRESHOLD )
442             sqr_n_basecase( prodp, up, size );
443         else {
444             mpi_ptr_t tspace;
445             tspace = mpi_alloc_limb_space( 2 * size );
446             sqr_n( prodp, up, size, tspace );
447             mpi_free_limb_space( tspace );
448         }
449     }
450     else {
451         if( size < KARATSUBA_THRESHOLD )
452             mul_n_basecase( prodp, up, vp, size );
453         else {
454             mpi_ptr_t tspace;
455             tspace = mpi_alloc_limb_space( 2 * size );
456             mul_n (prodp, up, vp, size, tspace);
457             mpi_free_limb_space( tspace );
458         }
459     }
460 }
461
462
463 /* Multiply the natural numbers u (pointed to by UP, with USIZE limbs)
464  * and v (pointed to by VP, with VSIZE limbs), and store the result at
465  * PRODP.  USIZE + VSIZE limbs are always stored, but if the input
466  * operands are normalized.  Return the most significant limb of the
467  * result.
468  *
469  * NOTE: The space pointed to by PRODP is overwritten before finished
470  * with U and V, so overlap is an error.
471  *
472  * Argument constraints:
473  * 1. USIZE >= VSIZE.
474  * 2. PRODP != UP and PRODP != VP, i.e. the destination
475  *    must be distinct from the multiplier and the multiplicand.
476  */
477
478 mpi_limb_t
479 mpihelp_mul( mpi_ptr_t prodp, mpi_ptr_t up, mpi_size_t usize,
480                               mpi_ptr_t vp, mpi_size_t vsize)
481 {
482     mpi_ptr_t prod_endp = prodp + usize + vsize - 1;
483     mpi_limb_t cy;
484     mpi_ptr_t tspace;
485
486     if( vsize < KARATSUBA_THRESHOLD ) {
487         mpi_size_t i;
488         mpi_limb_t v_limb;
489
490         if( !vsize )
491             return 0;
492
493         /* Multiply by the first limb in V separately, as the result can be
494          * stored (not added) to PROD.  We also avoid a loop for zeroing.  */
495         v_limb = vp[0];
496         if( v_limb <= 1 ) {
497             if( v_limb == 1 )
498                 MPN_COPY( prodp, up, usize );
499             else
500                 MPN_ZERO( prodp, usize );
501             cy = 0;
502         }
503         else
504             cy = mpihelp_mul_1( prodp, up, usize, v_limb );
505
506         prodp[usize] = cy;
507         prodp++;
508
509         /* For each iteration in the outer loop, multiply one limb from
510          * U with one limb from V, and add it to PROD.  */
511         for( i = 1; i < vsize; i++ ) {
512             v_limb = vp[i];
513             if( v_limb <= 1 ) {
514                 cy = 0;
515                 if( v_limb == 1 )
516                    cy = mpihelp_add_n(prodp, prodp, up, usize);
517             }
518             else
519                 cy = mpihelp_addmul_1(prodp, up, usize, v_limb);
520
521             prodp[usize] = cy;
522             prodp++;
523         }
524
525         return cy;
526     }
527
528     tspace = mpi_alloc_limb_space( 2 * vsize );
529     MPN_MUL_N_RECURSE( prodp, up, vp, vsize, tspace );
530
531     prodp += vsize;
532     up += vsize;
533     usize -= vsize;
534     if( usize >= vsize ) {
535         mpi_ptr_t tp = mpi_alloc_limb_space( 2 * vsize );
536         do {
537             MPN_MUL_N_RECURSE( tp, up, vp, vsize, tspace );
538             cy = mpihelp_add_n( prodp, prodp, tp, vsize );
539             mpihelp_add_1( prodp + vsize, tp + vsize, vsize, cy );
540             prodp += vsize;
541             up += vsize;
542             usize -= vsize;
543         } while( usize >= vsize );
544         mpi_free_limb_space( tp );
545     }
546
547     if( usize ) {
548         mpihelp_mul( tspace, vp, vsize, up, usize );
549         cy = mpihelp_add_n( prodp, prodp, tspace, vsize);
550         mpihelp_add_1( prodp + vsize, tspace + vsize, usize, cy );
551     }
552
553     mpi_free_limb_space( tspace );
554     return *prod_endp;
555 }
556
557