Blame mpz/and.c

Packit 5c3484
/* mpz_and -- Logical and.
Packit 5c3484
Packit 5c3484
Copyright 1991, 1993, 1994, 1996, 1997, 2000, 2001, 2003, 2005, 2012 Free
Packit 5c3484
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_and (mpz_ptr res, mpz_srcptr op1, mpz_srcptr op2)
Packit 5c3484
{
Packit 5c3484
  mp_srcptr op1_ptr, op2_ptr;
Packit 5c3484
  mp_size_t op1_size, op2_size;
Packit 5c3484
  mp_ptr res_ptr;
Packit 5c3484
  mp_size_t res_size;
Packit 5c3484
  mp_size_t i;
Packit 5c3484
  TMP_DECL;
Packit 5c3484
Packit 5c3484
  TMP_MARK;
Packit 5c3484
  op1_size = SIZ(op1);
Packit 5c3484
  op2_size = SIZ(op2);
Packit 5c3484
Packit 5c3484
  op1_ptr = PTR(op1);
Packit 5c3484
  op2_ptr = PTR(op2);
Packit 5c3484
Packit 5c3484
  if (op1_size >= 0)
Packit 5c3484
    {
Packit 5c3484
      if (op2_size >= 0)
Packit 5c3484
	{
Packit 5c3484
	  res_size = MIN (op1_size, op2_size);
Packit 5c3484
	  /* First loop finds the size of the result.  */
Packit 5c3484
	  for (i = res_size - 1; i >= 0; i--)
Packit 5c3484
	    if ((op1_ptr[i] & op2_ptr[i]) != 0)
Packit 5c3484
	      break;
Packit 5c3484
	  res_size = i + 1;
Packit 5c3484
Packit 5c3484
	  /* Handle allocation, now then we know exactly how much space is
Packit 5c3484
	     needed for the result.  */
Packit 5c3484
	  res_ptr = MPZ_REALLOC (res, res_size);
Packit 5c3484
	  /* Don't re-read op1_ptr and op2_ptr.  Since res_size <=
Packit 5c3484
	     MIN(op1_size, op2_size), res is not changed when op1
Packit 5c3484
	     is identical to res or op2 is identical to res.  */
Packit 5c3484
Packit 5c3484
	  SIZ(res) = res_size;
Packit 5c3484
	  if (LIKELY (res_size != 0))
Packit 5c3484
	    mpn_and_n (res_ptr, op1_ptr, op2_ptr, res_size);
Packit 5c3484
	  return;
Packit 5c3484
	}
Packit 5c3484
      else /* op2_size < 0 */
Packit 5c3484
	{
Packit 5c3484
	  /* Fall through to the code at the end of the function.  */
Packit 5c3484
	}
Packit 5c3484
    }
Packit 5c3484
  else
Packit 5c3484
    {
Packit 5c3484
      if (op2_size < 0)
Packit 5c3484
	{
Packit 5c3484
	  mp_ptr opx, opy;
Packit 5c3484
	  mp_limb_t cy;
Packit 5c3484
Packit 5c3484
	  /* Both operands are negative, so will be the result.
Packit 5c3484
	     -((-OP1) & (-OP2)) = -(~(OP1 - 1) & ~(OP2 - 1)) =
Packit 5c3484
	     = ~(~(OP1 - 1) & ~(OP2 - 1)) + 1 =
Packit 5c3484
	     = ((OP1 - 1) | (OP2 - 1)) + 1      */
Packit 5c3484
Packit 5c3484
	  /* It might seem as we could end up with an (invalid) result with
Packit 5c3484
	     a leading zero-limb here when one of the operands is of the
Packit 5c3484
	     type 1,,0,,..,,.0.  But some analysis shows that we surely
Packit 5c3484
	     would get carry into the zero-limb in this situation...  */
Packit 5c3484
Packit 5c3484
	  op1_size = -op1_size;
Packit 5c3484
	  op2_size = -op2_size;
Packit 5c3484
Packit 5c3484
	  if (op1_size > op2_size)
Packit 5c3484
	    MPN_SRCPTR_SWAP (op1_ptr, op1_size, op2_ptr, op2_size);
Packit 5c3484
Packit 5c3484
	  TMP_ALLOC_LIMBS_2 (opx, op1_size, opy, op2_size);
Packit 5c3484
	  mpn_sub_1 (opx, op1_ptr, op1_size, (mp_limb_t) 1);
Packit 5c3484
	  op1_ptr = opx;
Packit 5c3484
Packit 5c3484
	  mpn_sub_1 (opy, op2_ptr, op2_size, (mp_limb_t) 1);
Packit 5c3484
	  op2_ptr = opy;
Packit 5c3484
Packit 5c3484
	  res_ptr = MPZ_REALLOC (res, 1 + op2_size);
Packit 5c3484
	  /* Don't re-read OP1_PTR and OP2_PTR.  They point to temporary
Packit 5c3484
	     space--never to the space PTR(res) used to point to before
Packit 5c3484
	     reallocation.  */
Packit 5c3484
Packit 5c3484
	  MPN_COPY (res_ptr + op1_size, op2_ptr + op1_size,
Packit 5c3484
		    op2_size - op1_size);
Packit 5c3484
	  mpn_ior_n (res_ptr, op1_ptr, op2_ptr, op1_size);
Packit 5c3484
	  res_size = op2_size;
Packit 5c3484
Packit 5c3484
	  cy = mpn_add_1 (res_ptr, res_ptr, res_size, (mp_limb_t) 1);
Packit 5c3484
	  res_ptr[res_size] = cy;
Packit 5c3484
	  res_size += (cy != 0);
Packit 5c3484
Packit 5c3484
	  SIZ(res) = -res_size;
Packit 5c3484
	  TMP_FREE;
Packit 5c3484
	  return;
Packit 5c3484
	}
Packit 5c3484
      else
Packit 5c3484
	{
Packit 5c3484
	  /* We should compute -OP1 & OP2.  Swap OP1 and OP2 and fall
Packit 5c3484
	     through to the code that handles OP1 & -OP2.  */
Packit 5c3484
	  MPN_SRCPTR_SWAP (op1_ptr, op1_size, op2_ptr, op2_size);
Packit 5c3484
	}
Packit 5c3484
Packit 5c3484
    }
Packit 5c3484
Packit 5c3484
  {
Packit 5c3484
#if ANDNEW
Packit 5c3484
    mp_size_t op2_lim;
Packit 5c3484
    mp_size_t count;
Packit 5c3484
Packit 5c3484
    /* OP2 must be negated as with infinite precision.
Packit 5c3484
Packit 5c3484
       Scan from the low end for a non-zero limb.  The first non-zero
Packit 5c3484
       limb is simply negated (two's complement).  Any subsequent
Packit 5c3484
       limbs are one's complemented.  Of course, we don't need to
Packit 5c3484
       handle more limbs than there are limbs in the other, positive
Packit 5c3484
       operand as the result for those limbs is going to become zero
Packit 5c3484
       anyway.  */
Packit 5c3484
Packit 5c3484
    /* Scan for the least significant non-zero OP2 limb, and zero the
Packit 5c3484
       result meanwhile for those limb positions.  (We will surely
Packit 5c3484
       find a non-zero limb, so we can write the loop with one
Packit 5c3484
       termination condition only.)  */
Packit 5c3484
    for (i = 0; op2_ptr[i] == 0; i++)
Packit 5c3484
      res_ptr[i] = 0;
Packit 5c3484
    op2_lim = i;
Packit 5c3484
Packit 5c3484
    op2_size = -op2_size;
Packit 5c3484
Packit 5c3484
    if (op1_size <= op2_size)
Packit 5c3484
      {
Packit 5c3484
	/* The ones-extended OP2 is >= than the zero-extended OP1.
Packit 5c3484
	   RES_SIZE <= OP1_SIZE.  Find the exact size.  */
Packit 5c3484
	for (i = op1_size - 1; i > op2_lim; i--)
Packit 5c3484
	  if ((op1_ptr[i] & ~op2_ptr[i]) != 0)
Packit 5c3484
	    break;
Packit 5c3484
	res_size = i + 1;
Packit 5c3484
	for (i = res_size - 1; i > op2_lim; i--)
Packit 5c3484
	  res_ptr[i] = op1_ptr[i] & ~op2_ptr[i];
Packit 5c3484
	res_ptr[op2_lim] = op1_ptr[op2_lim] & -op2_ptr[op2_lim];
Packit 5c3484
	/* Yes, this *can* happen!  */
Packit 5c3484
	MPN_NORMALIZE (res_ptr, res_size);
Packit 5c3484
      }
Packit 5c3484
    else
Packit 5c3484
      {
Packit 5c3484
	/* The ones-extended OP2 is < than the zero-extended OP1.
Packit 5c3484
	   RES_SIZE == OP1_SIZE, since OP1 is normalized.  */
Packit 5c3484
	res_size = op1_size;
Packit 5c3484
	MPN_COPY (res_ptr + op2_size, op1_ptr + op2_size, op1_size - op2_size);
Packit 5c3484
	for (i = op2_size - 1; i > op2_lim; i--)
Packit 5c3484
	  res_ptr[i] = op1_ptr[i] & ~op2_ptr[i];
Packit 5c3484
	res_ptr[op2_lim] = op1_ptr[op2_lim] & -op2_ptr[op2_lim];
Packit 5c3484
      }
Packit 5c3484
Packit 5c3484
    SIZ(res) = res_size;
Packit 5c3484
#else
Packit 5c3484
Packit 5c3484
    /* OP1 is positive and zero-extended,
Packit 5c3484
       OP2 is negative and ones-extended.
Packit 5c3484
       The result will be positive.
Packit 5c3484
       OP1 & -OP2 = OP1 & ~(OP2 - 1).  */
Packit 5c3484
Packit 5c3484
    mp_ptr opx;
Packit 5c3484
Packit 5c3484
    op2_size = -op2_size;
Packit 5c3484
    opx = TMP_ALLOC_LIMBS (op2_size);
Packit 5c3484
    mpn_sub_1 (opx, op2_ptr, op2_size, (mp_limb_t) 1);
Packit 5c3484
    op2_ptr = opx;
Packit 5c3484
Packit 5c3484
    if (op1_size > op2_size)
Packit 5c3484
      {
Packit 5c3484
	/* The result has the same size as OP1, since OP1 is normalized
Packit 5c3484
	   and longer than the ones-extended OP2.  */
Packit 5c3484
	res_size = op1_size;
Packit 5c3484
Packit 5c3484
	/* Handle allocation, now then we know exactly how much space is
Packit 5c3484
	   needed for the result.  */
Packit 5c3484
	res_ptr = MPZ_REALLOC (res, res_size);
Packit 5c3484
	/* Don't re-read OP1_PTR or OP2_PTR.  Since res_size = op1_size,
Packit 5c3484
	   op1 is not changed if it is identical to res.
Packit 5c3484
	   OP2_PTR points to temporary space.  */
Packit 5c3484
Packit 5c3484
	MPN_COPY (res_ptr + op2_size, op1_ptr + op2_size, res_size - op2_size);
Packit 5c3484
	mpn_andn_n (res_ptr, op1_ptr, op2_ptr, op2_size);
Packit 5c3484
Packit 5c3484
	SIZ(res) = res_size;
Packit 5c3484
      }
Packit 5c3484
    else
Packit 5c3484
      {
Packit 5c3484
	/* Find out the exact result size.  Ignore the high limbs of OP2,
Packit 5c3484
	   OP1 is zero-extended and would make the result zero.  */
Packit 5c3484
	for (i = op1_size - 1; i >= 0; i--)
Packit 5c3484
	  if ((op1_ptr[i] & ~op2_ptr[i]) != 0)
Packit 5c3484
	    break;
Packit 5c3484
	res_size = i + 1;
Packit 5c3484
Packit 5c3484
	/* Handle allocation, now then we know exactly how much space is
Packit 5c3484
	   needed for the result.  */
Packit 5c3484
	res_ptr = MPZ_REALLOC (res, res_size);
Packit 5c3484
	/* Don't re-read OP1_PTR.  Since res_size <= op1_size,
Packit 5c3484
	   op1 is not changed if it is identical to res.
Packit 5c3484
	   Don't re-read OP2_PTR.  It points to temporary space--never
Packit 5c3484
	   to the space PTR(res) used to point to before reallocation.  */
Packit 5c3484
Packit 5c3484
	if (LIKELY (res_size != 0))
Packit 5c3484
	  mpn_andn_n (res_ptr, op1_ptr, op2_ptr, res_size);
Packit 5c3484
Packit 5c3484
	SIZ(res) = res_size;
Packit 5c3484
      }
Packit 5c3484
#endif
Packit 5c3484
  }
Packit 5c3484
  TMP_FREE;
Packit 5c3484
}