Blame mpz/hamdist.c

Packit 5c3484
/* mpz_hamdist -- calculate hamming distance.
Packit 5c3484
Packit 5c3484
Copyright 1994, 1996, 2001, 2002, 2009-2011 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
Packit 5c3484
mp_bitcnt_t
Packit 5c3484
mpz_hamdist (mpz_srcptr u, mpz_srcptr v) __GMP_NOTHROW
Packit 5c3484
{
Packit 5c3484
  mp_srcptr      up, vp;
Packit 5c3484
  mp_size_t      usize, vsize;
Packit 5c3484
  mp_bitcnt_t    count;
Packit 5c3484
Packit 5c3484
  usize = SIZ(u);
Packit 5c3484
  vsize = SIZ(v);
Packit 5c3484
Packit 5c3484
  up = PTR(u);
Packit 5c3484
  vp = PTR(v);
Packit 5c3484
Packit 5c3484
  if (usize >= 0)
Packit 5c3484
    {
Packit 5c3484
      if (vsize < 0)
Packit 5c3484
	return ~ (mp_bitcnt_t) 0;
Packit 5c3484
Packit 5c3484
      /* positive/positive */
Packit 5c3484
Packit 5c3484
      if (usize < vsize)
Packit 5c3484
	MPN_SRCPTR_SWAP (up,usize, vp,vsize);
Packit 5c3484
Packit 5c3484
      count = 0;
Packit 5c3484
      if (vsize != 0)
Packit 5c3484
	count = mpn_hamdist (up, vp, vsize);
Packit 5c3484
Packit 5c3484
      usize -= vsize;
Packit 5c3484
      if (usize != 0)
Packit 5c3484
	count += mpn_popcount (up + vsize, usize);
Packit 5c3484
Packit 5c3484
      return count;
Packit 5c3484
    }
Packit 5c3484
  else
Packit 5c3484
    {
Packit 5c3484
      mp_limb_t  ulimb, vlimb;
Packit 5c3484
      mp_size_t  old_vsize, step;
Packit 5c3484
Packit 5c3484
      if (vsize >= 0)
Packit 5c3484
	return ~ (mp_bitcnt_t) 0;
Packit 5c3484
Packit 5c3484
      /* negative/negative */
Packit 5c3484
Packit 5c3484
      usize = -usize;
Packit 5c3484
      vsize = -vsize;
Packit 5c3484
Packit 5c3484
      /* skip common low zeros */
Packit 5c3484
      for (;;)
Packit 5c3484
	{
Packit 5c3484
	  ASSERT (usize > 0);
Packit 5c3484
	  ASSERT (vsize > 0);
Packit 5c3484
Packit 5c3484
	  usize--;
Packit 5c3484
	  vsize--;
Packit 5c3484
Packit 5c3484
	  ulimb = *up++;
Packit 5c3484
	  vlimb = *vp++;
Packit 5c3484
Packit 5c3484
	  if (ulimb != 0)
Packit 5c3484
	    break;
Packit 5c3484
Packit 5c3484
	  if (vlimb != 0)
Packit 5c3484
	    {
Packit 5c3484
	      MPN_SRCPTR_SWAP (up,usize, vp,vsize);
Packit 5c3484
	      ulimb = vlimb;
Packit 5c3484
	      vlimb = 0;
Packit 5c3484
	      break;
Packit 5c3484
	    }
Packit 5c3484
	}
Packit 5c3484
Packit 5c3484
      /* twos complement first non-zero limbs (ulimb is non-zero, but vlimb
Packit 5c3484
	 might be zero) */
Packit 5c3484
      ulimb = -ulimb;
Packit 5c3484
      vlimb = -vlimb;
Packit 5c3484
      popc_limb (count, (ulimb ^ vlimb) & GMP_NUMB_MASK);
Packit 5c3484
Packit 5c3484
      if (vlimb == 0)
Packit 5c3484
	{
Packit 5c3484
	  mp_bitcnt_t  twoscount;
Packit 5c3484
Packit 5c3484
	  /* first non-zero of v */
Packit 5c3484
	  old_vsize = vsize;
Packit 5c3484
	  do
Packit 5c3484
	    {
Packit 5c3484
	      ASSERT (vsize > 0);
Packit 5c3484
	      vsize--;
Packit 5c3484
	      vlimb = *vp++;
Packit 5c3484
	    }
Packit 5c3484
	  while (vlimb == 0);
Packit 5c3484
Packit 5c3484
	  /* part of u corresponding to skipped v zeros */
Packit 5c3484
	  step = old_vsize - vsize - 1;
Packit 5c3484
	  count += step * GMP_NUMB_BITS;
Packit 5c3484
	  step = MIN (step, usize);
Packit 5c3484
	  if (step != 0)
Packit 5c3484
	    {
Packit 5c3484
	      count -= mpn_popcount (up, step);
Packit 5c3484
	      usize -= step;
Packit 5c3484
	      up += step;
Packit 5c3484
	    }
Packit 5c3484
Packit 5c3484
	  /* First non-zero vlimb as twos complement, xor with ones
Packit 5c3484
	     complement ulimb.  Note -v^(~0^u) == (v-1)^u. */
Packit 5c3484
	  vlimb--;
Packit 5c3484
	  if (usize != 0)
Packit 5c3484
	    {
Packit 5c3484
	      usize--;
Packit 5c3484
	      vlimb ^= *up++;
Packit 5c3484
	    }
Packit 5c3484
	  popc_limb (twoscount, vlimb);
Packit 5c3484
	  count += twoscount;
Packit 5c3484
	}
Packit 5c3484
Packit 5c3484
      /* Overlapping part of u and v, if any.  Ones complement both, so just
Packit 5c3484
	 plain hamdist. */
Packit 5c3484
      step = MIN (usize, vsize);
Packit 5c3484
      if (step != 0)
Packit 5c3484
	{
Packit 5c3484
	  count += mpn_hamdist (up, vp, step);
Packit 5c3484
	  usize -= step;
Packit 5c3484
	  vsize -= step;
Packit 5c3484
	  up += step;
Packit 5c3484
	  vp += step;
Packit 5c3484
	}
Packit 5c3484
Packit 5c3484
      /* Remaining high part of u or v, if any, ones complement but xor
Packit 5c3484
	 against all ones in the other, so plain popcount. */
Packit 5c3484
      if (usize != 0)
Packit 5c3484
	{
Packit 5c3484
	remaining:
Packit 5c3484
	  count += mpn_popcount (up, usize);
Packit 5c3484
	}
Packit 5c3484
      else if (vsize != 0)
Packit 5c3484
	{
Packit 5c3484
	  up = vp;
Packit 5c3484
	  usize = vsize;
Packit 5c3484
	  goto remaining;
Packit 5c3484
	}
Packit 5c3484
      return count;
Packit 5c3484
    }
Packit 5c3484
}