Blame mpz/combit.c

Packit 5c3484
/* mpz_combit -- complement a specified bit.
Packit 5c3484
Packit 5c3484
Copyright 2002, 2003, 2012, 2015 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 "gmp.h"
Packit 5c3484
#include "gmp-impl.h"
Packit 5c3484
Packit 5c3484
void
Packit 5c3484
mpz_combit (mpz_ptr d, mp_bitcnt_t bit_index)
Packit 5c3484
{
Packit 5c3484
  mp_size_t dsize = SIZ(d);
Packit 5c3484
  mp_ptr dp = PTR(d);
Packit 5c3484
Packit 5c3484
  mp_size_t limb_index = bit_index / GMP_NUMB_BITS;
Packit 5c3484
  mp_limb_t bit = (CNST_LIMB (1) << (bit_index % GMP_NUMB_BITS));
Packit 5c3484
Packit 5c3484
  /* Check for the most common case: Positive input, no realloc or
Packit 5c3484
     normalization needed. */
Packit 5c3484
  if (limb_index + 1 < dsize)
Packit 5c3484
    dp[limb_index] ^= bit;
Packit 5c3484
Packit 5c3484
  /* Check for the hairy case. d < 0, and we have all zero bits to the
Packit 5c3484
     right of the bit to toggle. */
Packit 5c3484
  else if (limb_index < -dsize
Packit 5c3484
	   && (limb_index == 0 || mpn_zero_p (dp, limb_index))
Packit 5c3484
	   && (dp[limb_index] & (bit - 1)) == 0)
Packit 5c3484
    {
Packit 5c3484
      ASSERT (dsize < 0);
Packit 5c3484
      dsize = -dsize;
Packit 5c3484
Packit 5c3484
      if (dp[limb_index] & bit)
Packit 5c3484
	{
Packit 5c3484
	  /* We toggle the least significant one bit. Corresponds to
Packit 5c3484
	     an add, with potential carry propagation, on the absolute
Packit 5c3484
	     value. */
Packit 5c3484
	  dp = MPZ_REALLOC (d, 1 + dsize);
Packit 5c3484
	  dp[dsize] = 0;
Packit 5c3484
	  MPN_INCR_U (dp + limb_index, 1 + dsize - limb_index, bit);
Packit 5c3484
	  SIZ(d) = - dsize - dp[dsize];
Packit 5c3484
	}
Packit 5c3484
      else
Packit 5c3484
	{
Packit 5c3484
	  /* We toggle a zero bit, subtract from the absolute value. */
Packit 5c3484
	  MPN_DECR_U (dp + limb_index, dsize - limb_index, bit);
Packit 5c3484
	  /* The absolute value shrinked by at most one bit. */
Packit 5c3484
	  dsize -= dp[dsize - 1] == 0;
Packit 5c3484
	  ASSERT (dsize > 0 && dp[dsize - 1] != 0);
Packit 5c3484
	  SIZ (d) = -dsize;
Packit 5c3484
	}
Packit 5c3484
    }
Packit 5c3484
  else
Packit 5c3484
    {
Packit 5c3484
      /* Simple case: Toggle the bit in the absolute value. */
Packit 5c3484
      dsize = ABS(dsize);
Packit 5c3484
      if (limb_index < dsize)
Packit 5c3484
	{
Packit 5c3484
	  mp_limb_t	 dlimb;
Packit 5c3484
	  dlimb = dp[limb_index] ^ bit;
Packit 5c3484
	  dp[limb_index] = dlimb;
Packit 5c3484
Packit 5c3484
	  /* Can happen only when limb_index = dsize - 1. Avoid SIZ(d)
Packit 5c3484
	     bookkeeping in the common case. */
Packit 5c3484
	  if (UNLIKELY ((dlimb == 0) + limb_index == dsize)) /* dsize == limb_index + 1 */
Packit 5c3484
	    {
Packit 5c3484
	      /* high limb became zero, must normalize */
Packit 5c3484
	      MPN_NORMALIZE (dp, limb_index);
Packit 5c3484
	      SIZ (d) = SIZ (d) >= 0 ? limb_index : -limb_index;
Packit 5c3484
	    }
Packit 5c3484
	}
Packit 5c3484
      else
Packit 5c3484
	{
Packit 5c3484
	  dp = MPZ_REALLOC (d, limb_index + 1);
Packit 5c3484
	  MPN_ZERO(dp + dsize, limb_index - dsize);
Packit 5c3484
	  dp[limb_index++] = bit;
Packit 5c3484
	  SIZ(d) = SIZ(d) >= 0 ? limb_index : -limb_index;
Packit 5c3484
	}
Packit 5c3484
    }
Packit 5c3484
}