14b6a78b7SSimon Schubert /* mpz_hamdist -- calculate hamming distance.
24b6a78b7SSimon Schubert
3*d2d4b659SJohn Marino Copyright 1994, 1996, 2001, 2002, 2009, 2010, 2011 Free Software Foundation,
4*d2d4b659SJohn Marino Inc.
54b6a78b7SSimon Schubert
64b6a78b7SSimon Schubert This file is part of the GNU MP Library.
74b6a78b7SSimon Schubert
84b6a78b7SSimon Schubert The GNU MP Library is free software; you can redistribute it and/or modify
94b6a78b7SSimon Schubert it under the terms of the GNU Lesser General Public License as published by
104b6a78b7SSimon Schubert the Free Software Foundation; either version 3 of the License, or (at your
114b6a78b7SSimon Schubert option) any later version.
124b6a78b7SSimon Schubert
134b6a78b7SSimon Schubert The GNU MP Library is distributed in the hope that it will be useful, but
144b6a78b7SSimon Schubert WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
154b6a78b7SSimon Schubert or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
164b6a78b7SSimon Schubert License for more details.
174b6a78b7SSimon Schubert
184b6a78b7SSimon Schubert You should have received a copy of the GNU Lesser General Public License
194b6a78b7SSimon Schubert along with the GNU MP Library. If not, see http://www.gnu.org/licenses/. */
204b6a78b7SSimon Schubert
214b6a78b7SSimon Schubert #include "gmp.h"
224b6a78b7SSimon Schubert #include "gmp-impl.h"
234b6a78b7SSimon Schubert
244b6a78b7SSimon Schubert
2554028e53SJohn Marino mp_bitcnt_t
mpz_hamdist(mpz_srcptr u,mpz_srcptr v)26*d2d4b659SJohn Marino mpz_hamdist (mpz_srcptr u, mpz_srcptr v) __GMP_NOTHROW
274b6a78b7SSimon Schubert {
284b6a78b7SSimon Schubert mp_srcptr up, vp;
294b6a78b7SSimon Schubert mp_size_t usize, vsize;
3054028e53SJohn Marino mp_bitcnt_t count;
314b6a78b7SSimon Schubert
324b6a78b7SSimon Schubert usize = SIZ(u);
334b6a78b7SSimon Schubert vsize = SIZ(v);
344b6a78b7SSimon Schubert
354b6a78b7SSimon Schubert up = PTR(u);
364b6a78b7SSimon Schubert vp = PTR(v);
374b6a78b7SSimon Schubert
384b6a78b7SSimon Schubert if (usize >= 0)
394b6a78b7SSimon Schubert {
404b6a78b7SSimon Schubert if (vsize < 0)
4154028e53SJohn Marino return ~ (mp_bitcnt_t) 0;
424b6a78b7SSimon Schubert
434b6a78b7SSimon Schubert /* positive/positive */
444b6a78b7SSimon Schubert
454b6a78b7SSimon Schubert if (usize < vsize)
464b6a78b7SSimon Schubert MPN_SRCPTR_SWAP (up,usize, vp,vsize);
474b6a78b7SSimon Schubert
484b6a78b7SSimon Schubert count = 0;
494b6a78b7SSimon Schubert if (vsize != 0)
504b6a78b7SSimon Schubert count = mpn_hamdist (up, vp, vsize);
514b6a78b7SSimon Schubert
524b6a78b7SSimon Schubert usize -= vsize;
534b6a78b7SSimon Schubert if (usize != 0)
544b6a78b7SSimon Schubert count += mpn_popcount (up + vsize, usize);
554b6a78b7SSimon Schubert
564b6a78b7SSimon Schubert return count;
574b6a78b7SSimon Schubert }
584b6a78b7SSimon Schubert else
594b6a78b7SSimon Schubert {
604b6a78b7SSimon Schubert mp_limb_t ulimb, vlimb;
614b6a78b7SSimon Schubert mp_size_t old_vsize, step;
624b6a78b7SSimon Schubert
634b6a78b7SSimon Schubert if (vsize >= 0)
64*d2d4b659SJohn Marino return ~ (mp_bitcnt_t) 0;
654b6a78b7SSimon Schubert
664b6a78b7SSimon Schubert /* negative/negative */
674b6a78b7SSimon Schubert
684b6a78b7SSimon Schubert usize = -usize;
694b6a78b7SSimon Schubert vsize = -vsize;
704b6a78b7SSimon Schubert
714b6a78b7SSimon Schubert /* skip common low zeros */
724b6a78b7SSimon Schubert for (;;)
734b6a78b7SSimon Schubert {
744b6a78b7SSimon Schubert ASSERT (usize > 0);
754b6a78b7SSimon Schubert ASSERT (vsize > 0);
764b6a78b7SSimon Schubert
774b6a78b7SSimon Schubert usize--;
784b6a78b7SSimon Schubert vsize--;
794b6a78b7SSimon Schubert
804b6a78b7SSimon Schubert ulimb = *up++;
814b6a78b7SSimon Schubert vlimb = *vp++;
824b6a78b7SSimon Schubert
834b6a78b7SSimon Schubert if (ulimb != 0)
844b6a78b7SSimon Schubert break;
854b6a78b7SSimon Schubert
864b6a78b7SSimon Schubert if (vlimb != 0)
874b6a78b7SSimon Schubert {
884b6a78b7SSimon Schubert MPN_SRCPTR_SWAP (up,usize, vp,vsize);
894b6a78b7SSimon Schubert ulimb = vlimb;
904b6a78b7SSimon Schubert vlimb = 0;
914b6a78b7SSimon Schubert break;
924b6a78b7SSimon Schubert }
934b6a78b7SSimon Schubert }
944b6a78b7SSimon Schubert
954b6a78b7SSimon Schubert /* twos complement first non-zero limbs (ulimb is non-zero, but vlimb
964b6a78b7SSimon Schubert might be zero) */
974b6a78b7SSimon Schubert ulimb = -ulimb;
984b6a78b7SSimon Schubert vlimb = -vlimb;
994b6a78b7SSimon Schubert popc_limb (count, (ulimb ^ vlimb) & GMP_NUMB_MASK);
1004b6a78b7SSimon Schubert
1014b6a78b7SSimon Schubert if (vlimb == 0)
1024b6a78b7SSimon Schubert {
10354028e53SJohn Marino mp_bitcnt_t twoscount;
1044b6a78b7SSimon Schubert
1054b6a78b7SSimon Schubert /* first non-zero of v */
1064b6a78b7SSimon Schubert old_vsize = vsize;
1074b6a78b7SSimon Schubert do
1084b6a78b7SSimon Schubert {
1094b6a78b7SSimon Schubert ASSERT (vsize > 0);
1104b6a78b7SSimon Schubert vsize--;
1114b6a78b7SSimon Schubert vlimb = *vp++;
1124b6a78b7SSimon Schubert }
1134b6a78b7SSimon Schubert while (vlimb == 0);
1144b6a78b7SSimon Schubert
1154b6a78b7SSimon Schubert /* part of u corresponding to skipped v zeros */
1164b6a78b7SSimon Schubert step = old_vsize - vsize - 1;
1174b6a78b7SSimon Schubert count += step * GMP_NUMB_BITS;
1184b6a78b7SSimon Schubert step = MIN (step, usize);
1194b6a78b7SSimon Schubert if (step != 0)
1204b6a78b7SSimon Schubert {
1214b6a78b7SSimon Schubert count -= mpn_popcount (up, step);
1224b6a78b7SSimon Schubert usize -= step;
1234b6a78b7SSimon Schubert up += step;
1244b6a78b7SSimon Schubert }
1254b6a78b7SSimon Schubert
1264b6a78b7SSimon Schubert /* First non-zero vlimb as twos complement, xor with ones
1274b6a78b7SSimon Schubert complement ulimb. Note -v^(~0^u) == (v-1)^u. */
1284b6a78b7SSimon Schubert vlimb--;
1294b6a78b7SSimon Schubert if (usize != 0)
1304b6a78b7SSimon Schubert {
1314b6a78b7SSimon Schubert usize--;
1324b6a78b7SSimon Schubert vlimb ^= *up++;
1334b6a78b7SSimon Schubert }
1344b6a78b7SSimon Schubert popc_limb (twoscount, vlimb);
1354b6a78b7SSimon Schubert count += twoscount;
1364b6a78b7SSimon Schubert }
1374b6a78b7SSimon Schubert
1384b6a78b7SSimon Schubert /* Overlapping part of u and v, if any. Ones complement both, so just
1394b6a78b7SSimon Schubert plain hamdist. */
1404b6a78b7SSimon Schubert step = MIN (usize, vsize);
1414b6a78b7SSimon Schubert if (step != 0)
1424b6a78b7SSimon Schubert {
1434b6a78b7SSimon Schubert count += mpn_hamdist (up, vp, step);
1444b6a78b7SSimon Schubert usize -= step;
1454b6a78b7SSimon Schubert vsize -= step;
1464b6a78b7SSimon Schubert up += step;
1474b6a78b7SSimon Schubert vp += step;
1484b6a78b7SSimon Schubert }
1494b6a78b7SSimon Schubert
1504b6a78b7SSimon Schubert /* Remaining high part of u or v, if any, ones complement but xor
1514b6a78b7SSimon Schubert against all ones in the other, so plain popcount. */
1524b6a78b7SSimon Schubert if (usize != 0)
1534b6a78b7SSimon Schubert {
1544b6a78b7SSimon Schubert remaining:
1554b6a78b7SSimon Schubert count += mpn_popcount (up, usize);
1564b6a78b7SSimon Schubert }
1574b6a78b7SSimon Schubert else if (vsize != 0)
1584b6a78b7SSimon Schubert {
1594b6a78b7SSimon Schubert up = vp;
1604b6a78b7SSimon Schubert usize = vsize;
1614b6a78b7SSimon Schubert goto remaining;
1624b6a78b7SSimon Schubert }
1634b6a78b7SSimon Schubert return count;
1644b6a78b7SSimon Schubert }
1654b6a78b7SSimon Schubert }
166