summaryrefslogtreecommitdiffstats
path: root/libtommath/bn_mp_jacobi.c
blob: c314c820f3f680d2221657f0370257df18a9b333 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
#include <tommath_private.h>
#ifdef BN_MP_JACOBI_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis
 *
 * LibTomMath is a library that provides multiple-precision
 * integer arithmetic as well as number theoretic functionality.
 *
 * The library was designed directly after the MPI library by
 * Michael Fromberger but has been written from scratch with
 * additional optimizations in place.
 *
 * The library is free for all purposes without any express
 * guarantee it works.
 *
 * Tom St Denis, tstdenis82@gmail.com, http://libtom.org
 */

/* computes the jacobi c = (a | n) (or Legendre if n is prime)
 * HAC pp. 73 Algorithm 2.149
 * HAC is wrong here, as the special case of (0 | 1) is not
 * handled correctly.
 */
int mp_jacobi(const mp_int *a, const mp_int *n, int *c)
{
   mp_int  a1, p1;
   int     k, s, r, res;
   mp_digit residue;

   /* if a < 0 return MP_VAL */
   if (mp_isneg(a) == MP_YES) {
      return MP_VAL;
   }

   /* if n <= 0 return MP_VAL */
   if (mp_cmp_d(n, 0) != MP_GT) {
      return MP_VAL;
   }

   /* step 1. handle case of a == 0 */
   if (mp_iszero(a) == MP_YES) {
      /* special case of a == 0 and n == 1 */
      if (mp_cmp_d(n, 1) == MP_EQ) {
         *c = 1;
      } else {
         *c = 0;
      }
      return MP_OKAY;
   }

   /* step 2.  if a == 1, return 1 */
   if (mp_cmp_d(a, 1) == MP_EQ) {
      *c = 1;
      return MP_OKAY;
   }

   /* default */
   s = 0;

   /* step 3.  write a = a1 * 2**k  */
   if ((res = mp_init_copy(&a1, a)) != MP_OKAY) {
      return res;
   }

   if ((res = mp_init(&p1)) != MP_OKAY) {
      goto LBL_A1;
   }

   /* divide out larger power of two */
   k = mp_cnt_lsb(&a1);
   if ((res = mp_div_2d(&a1, k, &a1, NULL)) != MP_OKAY) {
      goto LBL_P1;
   }

   /* step 4.  if e is even set s=1 */
   if ((k & 1) == 0) {
      s = 1;
   } else {
      /* else set s=1 if p = 1/7 (mod 8) or s=-1 if p = 3/5 (mod 8) */
      residue = n->dp[0] & 7;

      if ((residue == 1) || (residue == 7)) {
         s = 1;
      } else if ((residue == 3) || (residue == 5)) {
         s = -1;
      }
   }

   /* step 5.  if p == 3 (mod 4) *and* a1 == 3 (mod 4) then s = -s */
   if (((n->dp[0] & 3) == 3) && ((a1.dp[0] & 3) == 3)) {
      s = -s;
   }

   /* if a1 == 1 we're done */
   if (mp_cmp_d(&a1, 1) == MP_EQ) {
      *c = s;
   } else {
      /* n1 = n mod a1 */
      if ((res = mp_mod(n, &a1, &p1)) != MP_OKAY) {
         goto LBL_P1;
      }
      if ((res = mp_jacobi(&p1, &a1, &r)) != MP_OKAY) {
         goto LBL_P1;
      }
      *c = s * r;
   }

   /* done */
   res = MP_OKAY;
LBL_P1:
   mp_clear(&p1);
LBL_A1:
   mp_clear(&a1);
   return res;
}
#endif

/* ref:         $Format:%D$ */
/* git commit:  $Format:%H$ */
/* commit time: $Format:%ai$ */