195 lines
4.4 KiB
C
195 lines
4.4 KiB
C
/* gcd_subdiv_step.c.
|
|
|
|
THE FUNCTIONS IN THIS FILE ARE INTERNAL WITH MUTABLE INTERFACES. IT IS ONLY
|
|
SAFE TO REACH THEM THROUGH DOCUMENTED INTERFACES. IN FACT, IT IS ALMOST
|
|
GUARANTEED THAT THEY'LL CHANGE OR DISAPPEAR IN A FUTURE GNU MP RELEASE.
|
|
|
|
Copyright 2003, 2004, 2005, 2008, 2010, 2011 Free Software Foundation, Inc.
|
|
|
|
This file is part of the GNU MP Library.
|
|
|
|
The GNU MP Library is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU Lesser General Public License as published by
|
|
the Free Software Foundation; either version 3 of the License, or (at your
|
|
option) any later version.
|
|
|
|
The GNU MP Library is distributed in the hope that it will be useful, but
|
|
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
|
License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public License
|
|
along with the GNU MP Library. If not, see http://www.gnu.org/licenses/. */
|
|
|
|
#include <stdlib.h> /* for NULL */
|
|
|
|
#include "mpir.h"
|
|
#include "gmp-impl.h"
|
|
#include "longlong.h"
|
|
|
|
/* Used when mpn_hgcd or mpn_hgcd2 has failed. Then either one of a or
|
|
b is small, or the difference is small. Perform one subtraction
|
|
followed by one division. The normal case is to compute the reduced
|
|
a and b, and return the new size.
|
|
|
|
If s == 0 (used for gcd and gcdext), returns zero if the gcd is
|
|
found.
|
|
|
|
If s > 0, don't reduce to size <= s, and return zero if no
|
|
reduction is possible (if either a, b or |a-b| is of size <= s). */
|
|
|
|
/* The hook function is called as
|
|
|
|
hook(ctx, gp, gn, qp, qn, d)
|
|
|
|
in the following cases:
|
|
|
|
+ If A = B at the start, G is the gcd, Q is NULL, d = -1.
|
|
|
|
+ If one input is zero at the start, G is the gcd, Q is NULL,
|
|
d = 0 if A = G and d = 1 if B = G.
|
|
|
|
Otherwise, if d = 0 we have just subtracted a multiple of A from B,
|
|
and if d = 1 we have subtracted a multiple of B from A.
|
|
|
|
+ If A = B after subtraction, G is the gcd, Q is NULL.
|
|
|
|
+ If we get a zero remainder after division, G is the gcd, Q is the
|
|
quotient.
|
|
|
|
+ Otherwise, G is NULL, Q is the quotient (often 1).
|
|
|
|
*/
|
|
|
|
mp_size_t
|
|
mpn_gcd_subdiv_step (mp_ptr ap, mp_ptr bp, mp_size_t n, mp_size_t s,
|
|
gcd_subdiv_step_hook *hook, void *ctx,
|
|
mp_ptr tp)
|
|
{
|
|
static const mp_limb_t one = CNST_LIMB(1);
|
|
mp_size_t an, bn, qn;
|
|
|
|
int swapped;
|
|
|
|
ASSERT (n > 0);
|
|
ASSERT (ap[n-1] > 0 || bp[n-1] > 0);
|
|
|
|
an = bn = n;
|
|
MPN_NORMALIZE (ap, an);
|
|
MPN_NORMALIZE (bp, bn);
|
|
|
|
swapped = 0;
|
|
|
|
/* Arrange so that a < b, subtract b -= a, and maintain
|
|
normalization. */
|
|
if (an == bn)
|
|
{
|
|
int c;
|
|
MPN_CMP (c, ap, bp, an);
|
|
if (UNLIKELY (c == 0))
|
|
{
|
|
/* For gcdext, return the smallest of the two cofactors, so
|
|
pass d = -1. */
|
|
if (s == 0)
|
|
hook (ctx, ap, an, NULL, 0, -1);
|
|
return 0;
|
|
}
|
|
else if (c > 0)
|
|
{
|
|
MP_PTR_SWAP (ap, bp);
|
|
swapped ^= 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (an > bn)
|
|
{
|
|
MPN_PTR_SWAP (ap, an, bp, bn);
|
|
swapped ^= 1;
|
|
}
|
|
}
|
|
if (an <= s)
|
|
{
|
|
if (s == 0)
|
|
hook (ctx, bp, bn, NULL, 0, swapped ^ 1);
|
|
return 0;
|
|
}
|
|
|
|
ASSERT_NOCARRY (mpn_sub (bp, bp, bn, ap, an));
|
|
MPN_NORMALIZE (bp, bn);
|
|
ASSERT (bn > 0);
|
|
|
|
if (bn <= s)
|
|
{
|
|
/* Undo subtraction. */
|
|
mp_limb_t cy = mpn_add (bp, ap, an, bp, bn);
|
|
if (cy > 0)
|
|
bp[an] = cy;
|
|
return 0;
|
|
}
|
|
|
|
/* Arrange so that a < b */
|
|
if (an == bn)
|
|
{
|
|
int c;
|
|
MPN_CMP (c, ap, bp, an);
|
|
if (UNLIKELY (c == 0))
|
|
{
|
|
if (s > 0)
|
|
/* Just record subtraction and return */
|
|
hook (ctx, NULL, 0, &one, 1, swapped);
|
|
else
|
|
/* Found gcd. */
|
|
hook (ctx, bp, bn, NULL, 0, swapped);
|
|
return 0;
|
|
}
|
|
|
|
hook (ctx, NULL, 0, &one, 1, swapped);
|
|
|
|
if (c > 0)
|
|
{
|
|
MP_PTR_SWAP (ap, bp);
|
|
swapped ^= 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hook (ctx, NULL, 0, &one, 1, swapped);
|
|
|
|
if (an > bn)
|
|
{
|
|
MPN_PTR_SWAP (ap, an, bp, bn);
|
|
swapped ^= 1;
|
|
}
|
|
}
|
|
|
|
mpn_tdiv_qr (tp, bp, 0, bp, bn, ap, an);
|
|
qn = bn - an + 1;
|
|
bn = an;
|
|
MPN_NORMALIZE (bp, bn);
|
|
|
|
if (UNLIKELY (bn <= s))
|
|
{
|
|
if (s == 0)
|
|
{
|
|
hook (ctx, ap, an, tp, qn, swapped);
|
|
return 0;
|
|
}
|
|
|
|
/* Quotient is one too large, so decrement it and add back A. */
|
|
if (bn > 0)
|
|
{
|
|
mp_limb_t cy = mpn_add (bp, ap, an, bp, bn);
|
|
if (cy)
|
|
bp[an++] = cy;
|
|
}
|
|
else
|
|
MPN_COPY (bp, ap, an);
|
|
|
|
MPN_DECR_U (tp, qn, 1);
|
|
}
|
|
|
|
hook (ctx, NULL, 0, tp, qn, swapped);
|
|
return an;
|
|
}
|