Coverage Report

Created: 2024-06-28 06:19

/src/gmp-6.2.1/mpn/strongfibo.c
Line
Count
Source (jump to first uncovered line)
1
/* mpn_fib2m -- calculate Fibonacci numbers, modulo m.
2
3
Contributed to the GNU project by Marco Bodrato.
4
5
   THE FUNCTIONS IN THIS FILE ARE FOR INTERNAL USE ONLY.  THEY'RE ALMOST
6
   CERTAIN TO BE SUBJECT TO INCOMPATIBLE CHANGES OR DISAPPEAR COMPLETELY IN
7
   FUTURE GNU MP RELEASES.
8
9
Copyright 2001, 2002, 2005, 2009, 2018 Free Software Foundation, Inc.
10
11
This file is part of the GNU MP Library.
12
13
The GNU MP Library is free software; you can redistribute it and/or modify
14
it under the terms of either:
15
16
  * the GNU Lesser General Public License as published by the Free
17
    Software Foundation; either version 3 of the License, or (at your
18
    option) any later version.
19
20
or
21
22
  * the GNU General Public License as published by the Free Software
23
    Foundation; either version 2 of the License, or (at your option) any
24
    later version.
25
26
or both in parallel, as here.
27
28
The GNU MP Library is distributed in the hope that it will be useful, but
29
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
30
or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
31
for more details.
32
33
You should have received copies of the GNU General Public License and the
34
GNU Lesser General Public License along with the GNU MP Library.  If not,
35
see https://www.gnu.org/licenses/.  */
36
37
#include <stdio.h>
38
#include "gmp-impl.h"
39
40
41
#if HAVE_NATIVE_mpn_rsblsh1_n || HAVE_NATIVE_mpn_sublsh1_n
42
#else
43
/* Stores |{ap,n}-{bp,n}| in {rp,n},
44
   returns the sign of {ap,n}-{bp,n}. */
45
static int
46
abs_sub_n (mp_ptr rp, mp_srcptr ap, mp_srcptr bp, mp_size_t n)
47
{
48
  mp_limb_t  x, y;
49
  while (--n >= 0)
50
    {
51
      x = ap[n];
52
      y = bp[n];
53
      if (x != y)
54
        {
55
          ++n;
56
          if (x > y)
57
            {
58
              ASSERT_NOCARRY (mpn_sub_n (rp, ap, bp, n));
59
              return 1;
60
            }
61
          else
62
            {
63
              ASSERT_NOCARRY (mpn_sub_n (rp, bp, ap, n));
64
              return -1;
65
            }
66
        }
67
      rp[n] = 0;
68
    }
69
  return 0;
70
}
71
#endif
72
73
/* Computes at most count terms of the sequence needed by the
74
   Lucas-Lehmer-Riesel test, indexing backward:
75
   L_i = L_{i+1}^2 - 2
76
77
   The sequence is computed modulo M = {mp, mn}.
78
   The starting point is given in L_{count+1} = {lp, mn}.
79
   The scratch pointed by sp, needs a space of at least 3 * mn + 1 limbs.
80
81
   Returns the index i>0 if L_i = 0 (mod M) is found within the
82
   computed count terms of the sequence.  Otherwise it returns zero.
83
84
   Note: (+/-2)^2-2=2, (+/-1)^2-2=-1, 0^2-2=-2
85
 */
86
87
static mp_bitcnt_t
88
mpn_llriter (mp_ptr lp, mp_srcptr mp, mp_size_t mn, mp_bitcnt_t count, mp_ptr sp)
89
0
{
90
0
  do
91
0
    {
92
0
      mpn_sqr (sp, lp, mn);
93
0
      mpn_tdiv_qr (sp + 2 * mn, lp, 0, sp, 2 * mn, mp, mn);
94
0
      if (lp[0] < 5)
95
0
  {
96
    /* If L^2 % M < 5, |L^2 % M - 2| <= 2 */
97
0
    if (mn == 1 || mpn_zero_p (lp + 1, mn - 1))
98
0
      return (lp[0] == 2) ? count : 0;
99
0
    else
100
0
      MPN_DECR_U (lp, mn, 2);
101
0
  }
102
0
      else
103
0
  lp[0] -= 2;
104
0
    } while (--count != 0);
105
0
  return 0;
106
0
}
107
108
/* Store the Lucas' number L[n] at lp (maybe), computed modulo m.  lp
109
   and scratch should have room for mn*2+1 limbs.
110
111
   Returns the size of L[n] normally.
112
113
   If F[n] is zero modulo m, or L[n] is, returns 0 and lp is
114
   undefined.
115
*/
116
117
static mp_size_t
118
mpn_lucm (mp_ptr lp, mp_srcptr np, mp_size_t nn, mp_srcptr mp, mp_size_t mn, mp_ptr scratch)
119
0
{
120
0
  int   neg;
121
0
  mp_limb_t cy;
122
123
0
  ASSERT (! MPN_OVERLAP_P (lp, MAX(2*mn+1,5), scratch, MAX(2*mn+1,5)));
124
0
  ASSERT (nn > 0);
125
126
0
  neg = mpn_fib2m (lp, scratch, np, nn, mp, mn);
127
128
  /* F[n] = +/-{lp, mn}, F[n-1] = +/-{scratch, mn} */
129
0
  if (mpn_zero_p (lp, mn))
130
0
    return 0;
131
132
0
  if (neg) /* One sign is opposite, use sub instead of add. */
133
0
    {
134
0
#if HAVE_NATIVE_mpn_rsblsh1_n || HAVE_NATIVE_mpn_sublsh1_n
135
0
#if HAVE_NATIVE_mpn_rsblsh1_n
136
0
      cy = mpn_rsblsh1_n (lp, lp, scratch, mn); /* L[n] = +/-(2F[n-1]-(-F[n])) */
137
#else
138
      cy = mpn_sublsh1_n (lp, lp, scratch, mn); /* L[n] = -/+(F[n]-(-2F[n-1])) */
139
      if (cy != 0)
140
  cy = mpn_add_n (lp, lp, mp, mn) - cy;
141
#endif
142
0
      if (cy > 1)
143
0
  cy += mpn_add_n (lp, lp, mp, mn);
144
#else
145
      cy = mpn_lshift (scratch, scratch, mn, 1); /* 2F[n-1] */
146
      if (UNLIKELY (cy))
147
  cy -= mpn_sub_n (lp, scratch, lp, mn); /* L[n] = +/-(2F[n-1]-(-F[n])) */
148
      else
149
  abs_sub_n (lp, lp, scratch, mn);
150
#endif
151
0
      ASSERT (cy <= 1);
152
0
    }
153
0
  else
154
0
    {
155
0
#if HAVE_NATIVE_mpn_addlsh1_n
156
0
      cy = mpn_addlsh1_n (lp, lp, scratch, mn); /* L[n] = +/-(2F[n-1]+F[n])) */
157
#else
158
      cy = mpn_lshift (scratch, scratch, mn, 1);
159
      cy+= mpn_add_n (lp, lp, scratch, mn);
160
#endif
161
0
      ASSERT (cy <= 2);
162
0
    }
163
0
  while (cy || mpn_cmp (lp, mp, mn) >= 0)
164
0
    cy -= mpn_sub_n (lp, lp, mp, mn);
165
0
  MPN_NORMALIZE (lp, mn);
166
0
  return mn;
167
0
}
168
169
int
170
mpn_strongfibo (mp_srcptr mp, mp_size_t mn, mp_ptr scratch)
171
0
{
172
0
  mp_ptr  lp, sp;
173
0
  mp_size_t en;
174
0
  mp_bitcnt_t b0;
175
0
  TMP_DECL;
176
177
0
#if GMP_NUMB_BITS % 4 == 0
178
0
  b0 = mpn_scan0 (mp, 0);
179
#else
180
  {
181
    mpz_t m = MPZ_ROINIT_N(mp, mn);
182
    b0 = mpz_scan0 (m, 0);
183
  }
184
  if (UNLIKELY (b0 == mn * GMP_NUMB_BITS))
185
    {
186
      en = 1;
187
      scratch [0] = 1;
188
    }
189
  else
190
#endif
191
0
    {
192
0
      int cnt = b0 % GMP_NUMB_BITS;
193
0
      en = b0 / GMP_NUMB_BITS;
194
0
      if (LIKELY (cnt != 0))
195
0
  mpn_rshift (scratch, mp + en, mn - en, cnt);
196
0
      else
197
0
  MPN_COPY (scratch, mp + en, mn - en);
198
0
      en = mn - en;
199
0
      scratch [0] |= 1;
200
0
      en -= scratch [en - 1] == 0;
201
0
    }
202
0
  TMP_MARK;
203
204
0
  lp = TMP_ALLOC_LIMBS (4 * mn + 6);
205
0
  sp = lp + 2 * mn + 3;
206
0
  en = mpn_lucm (sp, scratch, en, mp, mn, lp);
207
0
  if (en != 0 && LIKELY (--b0 != 0))
208
0
    {
209
0
      mpn_sqr (lp, sp, en);
210
0
      lp [0] |= 2; /* V^2 + 2 */
211
0
      if (LIKELY (2 * en >= mn))
212
0
  mpn_tdiv_qr (sp, lp, 0, lp, 2 * en, mp, mn);
213
0
      else
214
0
  MPN_ZERO (lp + 2 * en, mn - 2 * en);
215
0
      if (! mpn_zero_p (lp, mn) && LIKELY (--b0 != 0))
216
0
  b0 = mpn_llriter (lp, mp, mn, b0, lp + mn + 1);
217
0
    }
218
0
  TMP_FREE;
219
0
  return (b0 != 0);
220
0
}