bn_s_mp_karatsuba_mul.c 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. #include "tommath_private.h"
  2. #ifdef BN_S_MP_KARATSUBA_MUL_C
  3. /* LibTomMath, multiple-precision integer library -- Tom St Denis */
  4. /* SPDX-License-Identifier: Unlicense */
  5. /* c = |a| * |b| using Karatsuba Multiplication using
  6. * three half size multiplications
  7. *
  8. * Let B represent the radix [e.g. 2**MP_DIGIT_BIT] and
  9. * let n represent half of the number of digits in
  10. * the min(a,b)
  11. *
  12. * a = a1 * B**n + a0
  13. * b = b1 * B**n + b0
  14. *
  15. * Then, a * b =>
  16. a1b1 * B**2n + ((a1 + a0)(b1 + b0) - (a0b0 + a1b1)) * B + a0b0
  17. *
  18. * Note that a1b1 and a0b0 are used twice and only need to be
  19. * computed once. So in total three half size (half # of
  20. * digit) multiplications are performed, a0b0, a1b1 and
  21. * (a1+b1)(a0+b0)
  22. *
  23. * Note that a multiplication of half the digits requires
  24. * 1/4th the number of single precision multiplications so in
  25. * total after one call 25% of the single precision multiplications
  26. * are saved. Note also that the call to mp_mul can end up back
  27. * in this function if the a0, a1, b0, or b1 are above the threshold.
  28. * This is known as divide-and-conquer and leads to the famous
  29. * O(N**lg(3)) or O(N**1.584) work which is asymptopically lower than
  30. * the standard O(N**2) that the baseline/comba methods use.
  31. * Generally though the overhead of this method doesn't pay off
  32. * until a certain size (N ~ 80) is reached.
  33. */
  34. mp_err s_mp_karatsuba_mul(const mp_int *a, const mp_int *b, mp_int *c)
  35. {
  36. mp_int x0, x1, y0, y1, t1, x0y0, x1y1;
  37. int B;
  38. mp_err err = MP_MEM; /* default the return code to an error */
  39. /* min # of digits */
  40. B = MP_MIN(a->used, b->used);
  41. /* now divide in two */
  42. B = B >> 1;
  43. /* init copy all the temps */
  44. if (mp_init_size(&x0, B) != MP_OKAY) {
  45. goto LBL_ERR;
  46. }
  47. if (mp_init_size(&x1, a->used - B) != MP_OKAY) {
  48. goto X0;
  49. }
  50. if (mp_init_size(&y0, B) != MP_OKAY) {
  51. goto X1;
  52. }
  53. if (mp_init_size(&y1, b->used - B) != MP_OKAY) {
  54. goto Y0;
  55. }
  56. /* init temps */
  57. if (mp_init_size(&t1, B * 2) != MP_OKAY) {
  58. goto Y1;
  59. }
  60. if (mp_init_size(&x0y0, B * 2) != MP_OKAY) {
  61. goto T1;
  62. }
  63. if (mp_init_size(&x1y1, B * 2) != MP_OKAY) {
  64. goto X0Y0;
  65. }
  66. /* now shift the digits */
  67. x0.used = y0.used = B;
  68. x1.used = a->used - B;
  69. y1.used = b->used - B;
  70. {
  71. int x;
  72. mp_digit *tmpa, *tmpb, *tmpx, *tmpy;
  73. /* we copy the digits directly instead of using higher level functions
  74. * since we also need to shift the digits
  75. */
  76. tmpa = a->dp;
  77. tmpb = b->dp;
  78. tmpx = x0.dp;
  79. tmpy = y0.dp;
  80. for (x = 0; x < B; x++) {
  81. *tmpx++ = *tmpa++;
  82. *tmpy++ = *tmpb++;
  83. }
  84. tmpx = x1.dp;
  85. for (x = B; x < a->used; x++) {
  86. *tmpx++ = *tmpa++;
  87. }
  88. tmpy = y1.dp;
  89. for (x = B; x < b->used; x++) {
  90. *tmpy++ = *tmpb++;
  91. }
  92. }
  93. /* only need to clamp the lower words since by definition the
  94. * upper words x1/y1 must have a known number of digits
  95. */
  96. mp_clamp(&x0);
  97. mp_clamp(&y0);
  98. /* now calc the products x0y0 and x1y1 */
  99. /* after this x0 is no longer required, free temp [x0==t2]! */
  100. if (mp_mul(&x0, &y0, &x0y0) != MP_OKAY) {
  101. goto X1Y1; /* x0y0 = x0*y0 */
  102. }
  103. if (mp_mul(&x1, &y1, &x1y1) != MP_OKAY) {
  104. goto X1Y1; /* x1y1 = x1*y1 */
  105. }
  106. /* now calc x1+x0 and y1+y0 */
  107. if (s_mp_add(&x1, &x0, &t1) != MP_OKAY) {
  108. goto X1Y1; /* t1 = x1 - x0 */
  109. }
  110. if (s_mp_add(&y1, &y0, &x0) != MP_OKAY) {
  111. goto X1Y1; /* t2 = y1 - y0 */
  112. }
  113. if (mp_mul(&t1, &x0, &t1) != MP_OKAY) {
  114. goto X1Y1; /* t1 = (x1 + x0) * (y1 + y0) */
  115. }
  116. /* add x0y0 */
  117. if (mp_add(&x0y0, &x1y1, &x0) != MP_OKAY) {
  118. goto X1Y1; /* t2 = x0y0 + x1y1 */
  119. }
  120. if (s_mp_sub(&t1, &x0, &t1) != MP_OKAY) {
  121. goto X1Y1; /* t1 = (x1+x0)*(y1+y0) - (x1y1 + x0y0) */
  122. }
  123. /* shift by B */
  124. if (mp_lshd(&t1, B) != MP_OKAY) {
  125. goto X1Y1; /* t1 = (x0y0 + x1y1 - (x1-x0)*(y1-y0))<<B */
  126. }
  127. if (mp_lshd(&x1y1, B * 2) != MP_OKAY) {
  128. goto X1Y1; /* x1y1 = x1y1 << 2*B */
  129. }
  130. if (mp_add(&x0y0, &t1, &t1) != MP_OKAY) {
  131. goto X1Y1; /* t1 = x0y0 + t1 */
  132. }
  133. if (mp_add(&t1, &x1y1, c) != MP_OKAY) {
  134. goto X1Y1; /* t1 = x0y0 + t1 + x1y1 */
  135. }
  136. /* Algorithm succeeded set the return code to MP_OKAY */
  137. err = MP_OKAY;
  138. X1Y1:
  139. mp_clear(&x1y1);
  140. X0Y0:
  141. mp_clear(&x0y0);
  142. T1:
  143. mp_clear(&t1);
  144. Y1:
  145. mp_clear(&y1);
  146. Y0:
  147. mp_clear(&y0);
  148. X1:
  149. mp_clear(&x1);
  150. X0:
  151. mp_clear(&x0);
  152. LBL_ERR:
  153. return err;
  154. }
  155. #endif