Blame mpz/fib_ui.c

Packit 5c3484
/* mpz_fib_ui -- calculate Fibonacci numbers.
Packit 5c3484
Packit 5c3484
Copyright 2000-2002, 2005, 2012, 2014 Free Software Foundation, Inc.
Packit 5c3484
Packit 5c3484
This file is part of the GNU MP Library.
Packit 5c3484
Packit 5c3484
The GNU MP Library is free software; you can redistribute it and/or modify
Packit 5c3484
it under the terms of either:
Packit 5c3484
Packit 5c3484
  * the GNU Lesser General Public License as published by the Free
Packit 5c3484
    Software Foundation; either version 3 of the License, or (at your
Packit 5c3484
    option) any later version.
Packit 5c3484
Packit 5c3484
or
Packit 5c3484
Packit 5c3484
  * the GNU General Public License as published by the Free Software
Packit 5c3484
    Foundation; either version 2 of the License, or (at your option) any
Packit 5c3484
    later version.
Packit 5c3484
Packit 5c3484
or both in parallel, as here.
Packit 5c3484
Packit 5c3484
The GNU MP Library is distributed in the hope that it will be useful, but
Packit 5c3484
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
Packit 5c3484
or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
Packit 5c3484
for more details.
Packit 5c3484
Packit 5c3484
You should have received copies of the GNU General Public License and the
Packit 5c3484
GNU Lesser General Public License along with the GNU MP Library.  If not,
Packit 5c3484
see https://www.gnu.org/licenses/.  */
Packit 5c3484
Packit 5c3484
#include <stdio.h>
Packit 5c3484
#include "gmp.h"
Packit 5c3484
#include "gmp-impl.h"
Packit 5c3484
#include "longlong.h"
Packit 5c3484
Packit 5c3484
Packit 5c3484
/* change to "#define TRACE(x) x" to get some traces */
Packit 5c3484
#define TRACE(x)
Packit 5c3484
Packit 5c3484
Packit 5c3484
/* In the F[2k+1] below for k odd, the -2 won't give a borrow from the low
Packit 5c3484
   limb because the result F[2k+1] is an F[4m+3] and such numbers are always
Packit 5c3484
   == 1, 2 or 5 mod 8, whereas an underflow would leave 6 or 7.  (This is
Packit 5c3484
   the same as in mpn_fib2_ui.)
Packit 5c3484
Packit 5c3484
   In the F[2k+1] for k even, the +2 won't give a carry out of the low limb
Packit 5c3484
   in normal circumstances.  This is an F[4m+1] and we claim that F[3*2^b+1]
Packit 5c3484
   == 1 mod 2^b is the first F[4m+1] congruent to 0 or 1 mod 2^b, and hence
Packit 5c3484
   if n < 2^GMP_NUMB_BITS then F[n] cannot have a low limb of 0 or 1.  No
Packit 5c3484
   proof for this claim, but it's been verified up to b==32 and has such a
Packit 5c3484
   nice pattern it must be true :-).  Of interest is that F[3*2^b] == 0 mod
Packit 5c3484
   2^(b+1) seems to hold too.
Packit 5c3484
Packit 5c3484
   When n >= 2^GMP_NUMB_BITS, which can arise in a nails build, then the low
Packit 5c3484
   limb of F[4m+1] can certainly be 1, and an mpn_add_1 must be used.  */
Packit 5c3484
Packit 5c3484
void
Packit 5c3484
mpz_fib_ui (mpz_ptr fn, unsigned long n)
Packit 5c3484
{
Packit 5c3484
  mp_ptr         fp, xp, yp;
Packit 5c3484
  mp_size_t      size, xalloc;
Packit 5c3484
  unsigned long  n2;
Packit 5c3484
  mp_limb_t      c;
Packit 5c3484
  TMP_DECL;
Packit 5c3484
Packit 5c3484
  if (n <= FIB_TABLE_LIMIT)
Packit 5c3484
    {
Packit 5c3484
      PTR(fn)[0] = FIB_TABLE (n);
Packit 5c3484
      SIZ(fn) = (n != 0);      /* F[0]==0, others are !=0 */
Packit 5c3484
      return;
Packit 5c3484
    }
Packit 5c3484
Packit 5c3484
  n2 = n/2;
Packit 5c3484
  xalloc = MPN_FIB2_SIZE (n2) + 1;
Packit 5c3484
  fp = MPZ_NEWALLOC (fn, 2 * xalloc);
Packit 5c3484
Packit 5c3484
  TMP_MARK;
Packit 5c3484
  TMP_ALLOC_LIMBS_2 (xp,xalloc, yp,xalloc);
Packit 5c3484
  size = mpn_fib2_ui (xp, yp, n2);
Packit 5c3484
Packit 5c3484
  TRACE (printf ("mpz_fib_ui last step n=%lu size=%ld bit=%lu\n",
Packit 5c3484
		 n >> 1, size, n&1;;
Packit 5c3484
	 mpn_trace ("xp", xp, size);
Packit 5c3484
	 mpn_trace ("yp", yp, size));
Packit 5c3484
Packit 5c3484
  if (n & 1)
Packit 5c3484
    {
Packit 5c3484
      /* F[2k+1] = (2F[k]+F[k-1])*(2F[k]-F[k-1]) + 2*(-1)^k  */
Packit 5c3484
      mp_size_t  xsize, ysize;
Packit 5c3484
Packit 5c3484
#if HAVE_NATIVE_mpn_add_n_sub_n
Packit 5c3484
      xp[size] = mpn_lshift (xp, xp, size, 1);
Packit 5c3484
      yp[size] = 0;
Packit 5c3484
      ASSERT_NOCARRY (mpn_add_n_sub_n (xp, yp, xp, yp, size+1));
Packit 5c3484
      xsize = size + (xp[size] != 0);
Packit 5c3484
      ASSERT (yp[size] <= 1);
Packit 5c3484
      ysize = size + yp[size];
Packit 5c3484
#else
Packit 5c3484
      mp_limb_t  c2;
Packit 5c3484
Packit 5c3484
      c2 = mpn_lshift (fp, xp, size, 1);
Packit 5c3484
      c = c2 + mpn_add_n (xp, fp, yp, size);
Packit 5c3484
      xp[size] = c;
Packit 5c3484
      xsize = size + (c != 0);
Packit 5c3484
      c2 -= mpn_sub_n (yp, fp, yp, size);
Packit 5c3484
      yp[size] = c2;
Packit 5c3484
      ASSERT (c2 <= 1);
Packit 5c3484
      ysize = size + c2;
Packit 5c3484
#endif
Packit 5c3484
Packit 5c3484
      size = xsize + ysize;
Packit 5c3484
      c = mpn_mul (fp, xp, xsize, yp, ysize);
Packit 5c3484
Packit 5c3484
#if GMP_NUMB_BITS >= BITS_PER_ULONG
Packit 5c3484
      /* no overflow, see comments above */
Packit 5c3484
      ASSERT (n & 2 ? fp[0] >= 2 : fp[0] <= GMP_NUMB_MAX-2);
Packit 5c3484
      fp[0] += (n & 2 ? -CNST_LIMB(2) : CNST_LIMB(2));
Packit 5c3484
#else
Packit 5c3484
      if (n & 2)
Packit 5c3484
	{
Packit 5c3484
	  ASSERT (fp[0] >= 2);
Packit 5c3484
	  fp[0] -= 2;
Packit 5c3484
	}
Packit 5c3484
      else
Packit 5c3484
	{
Packit 5c3484
	  ASSERT (c != GMP_NUMB_MAX); /* because it's the high of a mul */
Packit 5c3484
	  c += mpn_add_1 (fp, fp, size-1, CNST_LIMB(2));
Packit 5c3484
	  fp[size-1] = c;
Packit 5c3484
	}
Packit 5c3484
#endif
Packit 5c3484
    }
Packit 5c3484
  else
Packit 5c3484
    {
Packit 5c3484
      /* F[2k] = F[k]*(F[k]+2F[k-1]) */
Packit 5c3484
Packit 5c3484
      mp_size_t  xsize, ysize;
Packit 5c3484
#if HAVE_NATIVE_mpn_addlsh1_n
Packit 5c3484
      c = mpn_addlsh1_n (yp, xp, yp, size);
Packit 5c3484
#else
Packit 5c3484
      c = mpn_lshift (yp, yp, size, 1);
Packit 5c3484
      c += mpn_add_n (yp, yp, xp, size);
Packit 5c3484
#endif
Packit 5c3484
      yp[size] = c;
Packit 5c3484
      xsize = size;
Packit 5c3484
      ysize = size + (c != 0);
Packit 5c3484
      size += ysize;
Packit 5c3484
      c = mpn_mul (fp, yp, ysize, xp, xsize);
Packit 5c3484
    }
Packit 5c3484
Packit 5c3484
  /* one or two high zeros */
Packit 5c3484
  size -= (c == 0);
Packit 5c3484
  size -= (fp[size-1] == 0);
Packit 5c3484
  SIZ(fn) = size;
Packit 5c3484
Packit 5c3484
  TRACE (printf ("done special, size=%ld\n", size);
Packit 5c3484
	 mpn_trace ("fp ", fp, size));
Packit 5c3484
Packit 5c3484
  TMP_FREE;
Packit 5c3484
}