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
abs_sub_n(mp_ptr rp,mp_srcptr ap,mp_srcptr bp,mp_size_t n)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
mpn_llriter(mp_ptr lp,mp_srcptr mp,mp_size_t mn,mp_bitcnt_t count,mp_ptr sp)88 mpn_llriter (mp_ptr lp, mp_srcptr mp, mp_size_t mn, mp_bitcnt_t count, mp_ptr sp)
89 {
90   do
91     {
92       mpn_sqr (sp, lp, mn);
93       mpn_tdiv_qr (sp + 2 * mn, lp, 0, sp, 2 * mn, mp, mn);
94       if (lp[0] < 5)
95 	{
96 	  /* If L^2 % M < 5, |L^2 % M - 2| <= 2 */
97 	  if (mn == 1 || mpn_zero_p (lp + 1, mn - 1))
98 	    return (lp[0] == 2) ? count : 0;
99 	  else
100 	    MPN_DECR_U (lp, mn, 2);
101 	}
102       else
103 	lp[0] -= 2;
104     } while (--count != 0);
105   return 0;
106 }
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
mpn_lucm(mp_ptr lp,mp_srcptr np,mp_size_t nn,mp_srcptr mp,mp_size_t mn,mp_ptr scratch)118 mpn_lucm (mp_ptr lp, mp_srcptr np, mp_size_t nn, mp_srcptr mp, mp_size_t mn, mp_ptr scratch)
119 {
120   int		neg;
121   mp_limb_t	cy;
122 
123   ASSERT (! MPN_OVERLAP_P (lp, MAX(2*mn+1,5), scratch, MAX(2*mn+1,5)));
124   ASSERT (nn > 0);
125 
126   neg = mpn_fib2m (lp, scratch, np, nn, mp, mn);
127 
128   /* F[n] = +/-{lp, mn}, F[n-1] = +/-{scratch, mn} */
129   if (mpn_zero_p (lp, mn))
130     return 0;
131 
132   if (neg) /* One sign is opposite, use sub instead of add. */
133     {
134 #if HAVE_NATIVE_mpn_rsblsh1_n || HAVE_NATIVE_mpn_sublsh1_n
135 #if HAVE_NATIVE_mpn_rsblsh1_n
136       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       if (cy > 1)
143 	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       ASSERT (cy <= 1);
152     }
153   else
154     {
155 #if HAVE_NATIVE_mpn_addlsh1_n
156       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       ASSERT (cy <= 2);
162     }
163   while (cy || mpn_cmp (lp, mp, mn) >= 0)
164     cy -= mpn_sub_n (lp, lp, mp, mn);
165   MPN_NORMALIZE (lp, mn);
166   return mn;
167 }
168 
169 int
mpn_strongfibo(mp_srcptr mp,mp_size_t mn,mp_ptr scratch)170 mpn_strongfibo (mp_srcptr mp, mp_size_t mn, mp_ptr scratch)
171 {
172   mp_ptr	lp, sp;
173   mp_size_t	en;
174   mp_bitcnt_t	b0;
175   TMP_DECL;
176 
177 #if GMP_NUMB_BITS % 4 == 0
178   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     {
192       int cnt = b0 % GMP_NUMB_BITS;
193       en = b0 / GMP_NUMB_BITS;
194       if (LIKELY (cnt != 0))
195 	mpn_rshift (scratch, mp + en, mn - en, cnt);
196       else
197 	MPN_COPY (scratch, mp + en, mn - en);
198       en = mn - en;
199       scratch [0] |= 1;
200       en -= scratch [en - 1] == 0;
201     }
202   TMP_MARK;
203 
204   lp = TMP_ALLOC_LIMBS (4 * mn + 6);
205   sp = lp + 2 * mn + 3;
206   en = mpn_lucm (sp, scratch, en, mp, mn, lp);
207   if (en != 0 && LIKELY (--b0 != 0))
208     {
209       mpn_sqr (lp, sp, en);
210       lp [0] |= 2; /* V^2 + 2 */
211       if (LIKELY (2 * en >= mn))
212 	mpn_tdiv_qr (sp, lp, 0, lp, 2 * en, mp, mn);
213       else
214 	MPN_ZERO (lp + 2 * en, mn - 2 * en);
215       if (! mpn_zero_p (lp, mn) && LIKELY (--b0 != 0))
216 	b0 = mpn_llriter (lp, mp, mn, b0, lp + mn + 1);
217     }
218   TMP_FREE;
219   return (b0 != 0);
220 }
221