Blame mpz/lucnum_ui.c

Packit 5c3484
/* mpz_lucnum_ui -- calculate Lucas number.
Packit 5c3484
Packit 5c3484
Copyright 2001, 2003, 2005, 2011, 2012 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
Packit 5c3484
Packit 5c3484
/* change this to "#define TRACE(x) x" for diagnostics */
Packit 5c3484
#define TRACE(x)
Packit 5c3484
Packit 5c3484
Packit 5c3484
/* Notes:
Packit 5c3484
Packit 5c3484
   For the +4 in L[2k+1] when k is even, all L[4m+3] == 4, 5 or 7 mod 8, so
Packit 5c3484
   there can't be an overflow applying +4 to just the low limb (since that
Packit 5c3484
   would leave 0, 1, 2 or 3 mod 8).
Packit 5c3484
Packit 5c3484
   For the -4 in L[2k+1] when k is even, it seems (no proof) that
Packit 5c3484
   L[3*2^(b-2)-3] == -4 mod 2^b, so for instance with a 32-bit limb
Packit 5c3484
   L[0xBFFFFFFD] == 0xFFFFFFFC mod 2^32, and this implies a borrow from the
Packit 5c3484
   low limb.  Obviously L[0xBFFFFFFD] is a huge number, but it's at least
Packit 5c3484
   conceivable to calculate it, so it probably should be handled.
Packit 5c3484
Packit 5c3484
   For the -2 in L[2k] with k even, it seems (no proof) L[2^(b-1)] == -1 mod
Packit 5c3484
   2^b, so for instance in 32-bits L[0x80000000] has a low limb of
Packit 5c3484
   0xFFFFFFFF so there would have been a borrow.  Again L[0x80000000] is
Packit 5c3484
   obviously huge, but probably should be made to work.  */
Packit 5c3484
Packit 5c3484
void
Packit 5c3484
mpz_lucnum_ui (mpz_ptr ln, unsigned long n)
Packit 5c3484
{
Packit 5c3484
  mp_size_t  lalloc, xalloc, lsize, xsize;
Packit 5c3484
  mp_ptr     lp, xp;
Packit 5c3484
  mp_limb_t  c;
Packit 5c3484
  int        zeros;
Packit 5c3484
  TMP_DECL;
Packit 5c3484
Packit 5c3484
  TRACE (printf ("mpn_lucnum_ui n=%lu\n", n));
Packit 5c3484
Packit 5c3484
  if (n <= FIB_TABLE_LUCNUM_LIMIT)
Packit 5c3484
    {
Packit 5c3484
      /* L[n] = F[n] + 2F[n-1] */
Packit 5c3484
      PTR(ln)[0] = FIB_TABLE(n) + 2 * FIB_TABLE ((int) n - 1);
Packit 5c3484
      SIZ(ln) = 1;
Packit 5c3484
      return;
Packit 5c3484
    }
Packit 5c3484
Packit 5c3484
  /* +1 since L[n]=F[n]+2F[n-1] might be 1 limb bigger than F[n], further +1
Packit 5c3484
     since square or mul used below might need an extra limb over the true
Packit 5c3484
     size */
Packit 5c3484
  lalloc = MPN_FIB2_SIZE (n) + 2;
Packit 5c3484
  lp = MPZ_REALLOC (ln, lalloc);
Packit 5c3484
Packit 5c3484
  TMP_MARK;
Packit 5c3484
  xalloc = lalloc;
Packit 5c3484
  xp = TMP_ALLOC_LIMBS (xalloc);
Packit 5c3484
Packit 5c3484
  /* Strip trailing zeros from n, until either an odd number is reached
Packit 5c3484
     where the L[2k+1] formula can be used, or until n fits within the
Packit 5c3484
     FIB_TABLE data.  The table is preferred of course.  */
Packit 5c3484
  zeros = 0;
Packit 5c3484
  for (;;)
Packit 5c3484
    {
Packit 5c3484
      if (n & 1)
Packit 5c3484
	{
Packit 5c3484
	  /* L[2k+1] = 5*F[k-1]*(2*F[k]+F[k-1]) - 4*(-1)^k */
Packit 5c3484
Packit 5c3484
	  mp_size_t  yalloc, ysize;
Packit 5c3484
	  mp_ptr     yp;
Packit 5c3484
Packit 5c3484
	  TRACE (printf ("  initial odd n=%lu\n", n));
Packit 5c3484
Packit 5c3484
	  yalloc = MPN_FIB2_SIZE (n/2);
Packit 5c3484
	  yp = TMP_ALLOC_LIMBS (yalloc);
Packit 5c3484
	  ASSERT (xalloc >= yalloc);
Packit 5c3484
Packit 5c3484
	  xsize = mpn_fib2_ui (xp, yp, n/2);
Packit 5c3484
Packit 5c3484
	  /* possible high zero on F[k-1] */
Packit 5c3484
	  ysize = xsize;
Packit 5c3484
	  ysize -= (yp[ysize-1] == 0);
Packit 5c3484
	  ASSERT (yp[ysize-1] != 0);
Packit 5c3484
Packit 5c3484
	  /* xp = 2*F[k] + F[k-1] */
Packit 5c3484
#if HAVE_NATIVE_mpn_addlsh1_n
Packit 5c3484
	  c = mpn_addlsh1_n (xp, yp, xp, xsize);
Packit 5c3484
#else
Packit 5c3484
	  c = mpn_lshift (xp, xp, xsize, 1);
Packit 5c3484
	  c += mpn_add_n (xp, xp, yp, xsize);
Packit 5c3484
#endif
Packit 5c3484
	  ASSERT (xalloc >= xsize+1);
Packit 5c3484
	  xp[xsize] = c;
Packit 5c3484
	  xsize += (c != 0);
Packit 5c3484
	  ASSERT (xp[xsize-1] != 0);
Packit 5c3484
Packit 5c3484
	  ASSERT (lalloc >= xsize + ysize);
Packit 5c3484
	  c = mpn_mul (lp, xp, xsize, yp, ysize);
Packit 5c3484
	  lsize = xsize + ysize;
Packit 5c3484
	  lsize -= (c == 0);
Packit 5c3484
Packit 5c3484
	  /* lp = 5*lp */
Packit 5c3484
#if HAVE_NATIVE_mpn_addlsh2_n
Packit 5c3484
	  c = mpn_addlsh2_n (lp, lp, lp, lsize);
Packit 5c3484
#else
Packit 5c3484
	  /* FIXME: Is this faster than mpn_mul_1 ? */
Packit 5c3484
	  c = mpn_lshift (xp, lp, lsize, 2);
Packit 5c3484
	  c += mpn_add_n (lp, lp, xp, lsize);
Packit 5c3484
#endif
Packit 5c3484
	  ASSERT (lalloc >= lsize+1);
Packit 5c3484
	  lp[lsize] = c;
Packit 5c3484
	  lsize += (c != 0);
Packit 5c3484
Packit 5c3484
	  /* lp = lp - 4*(-1)^k */
Packit 5c3484
	  if (n & 2)
Packit 5c3484
	    {
Packit 5c3484
	      /* no overflow, see comments above */
Packit 5c3484
	      ASSERT (lp[0] <= MP_LIMB_T_MAX-4);
Packit 5c3484
	      lp[0] += 4;
Packit 5c3484
	    }
Packit 5c3484
	  else
Packit 5c3484
	    {
Packit 5c3484
	      /* won't go negative */
Packit 5c3484
	      MPN_DECR_U (lp, lsize, CNST_LIMB(4));
Packit 5c3484
	    }
Packit 5c3484
Packit 5c3484
	  TRACE (mpn_trace ("  l",lp, lsize));
Packit 5c3484
	  break;
Packit 5c3484
	}
Packit 5c3484
Packit 5c3484
      MP_PTR_SWAP (xp, lp); /* balance the swaps wanted in the L[2k] below */
Packit 5c3484
      zeros++;
Packit 5c3484
      n /= 2;
Packit 5c3484
Packit 5c3484
      if (n <= FIB_TABLE_LUCNUM_LIMIT)
Packit 5c3484
	{
Packit 5c3484
	  /* L[n] = F[n] + 2F[n-1] */
Packit 5c3484
	  lp[0] = FIB_TABLE (n) + 2 * FIB_TABLE ((int) n - 1);
Packit 5c3484
	  lsize = 1;
Packit 5c3484
Packit 5c3484
	  TRACE (printf ("  initial small n=%lu\n", n);
Packit 5c3484
		 mpn_trace ("  l",lp, lsize));
Packit 5c3484
	  break;
Packit 5c3484
	}
Packit 5c3484
    }
Packit 5c3484
Packit 5c3484
  for ( ; zeros != 0; zeros--)
Packit 5c3484
    {
Packit 5c3484
      /* L[2k] = L[k]^2 + 2*(-1)^k */
Packit 5c3484
Packit 5c3484
      TRACE (printf ("  zeros=%d\n", zeros));
Packit 5c3484
Packit 5c3484
      ASSERT (xalloc >= 2*lsize);
Packit 5c3484
      mpn_sqr (xp, lp, lsize);
Packit 5c3484
      lsize *= 2;
Packit 5c3484
      lsize -= (xp[lsize-1] == 0);
Packit 5c3484
Packit 5c3484
      /* First time around the loop k==n determines (-1)^k, after that k is
Packit 5c3484
	 always even and we set n=0 to indicate that.  */
Packit 5c3484
      if (n & 1)
Packit 5c3484
	{
Packit 5c3484
	  /* L[n]^2 == 0 or 1 mod 4, like all squares, so +2 gives no carry */
Packit 5c3484
	  ASSERT (xp[0] <= MP_LIMB_T_MAX-2);
Packit 5c3484
	  xp[0] += 2;
Packit 5c3484
	  n = 0;
Packit 5c3484
	}
Packit 5c3484
      else
Packit 5c3484
	{
Packit 5c3484
	  /* won't go negative */
Packit 5c3484
	  MPN_DECR_U (xp, lsize, CNST_LIMB(2));
Packit 5c3484
	}
Packit 5c3484
Packit 5c3484
      MP_PTR_SWAP (xp, lp);
Packit 5c3484
      ASSERT (lp[lsize-1] != 0);
Packit 5c3484
    }
Packit 5c3484
Packit 5c3484
  /* should end up in the right spot after all the xp/lp swaps */
Packit 5c3484
  ASSERT (lp == PTR(ln));
Packit 5c3484
  SIZ(ln) = lsize;
Packit 5c3484
Packit 5c3484
  TMP_FREE;
Packit 5c3484
}