Home | History | Annotate | Line # | Download | only in generic
      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 {
     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
    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
    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