mirror of
https://review.haiku-os.org/buildtools
synced 2025-02-12 08:47:41 +01:00
Change-Id: Iae65e95cfa0d92091b8b0a424ae36d88efa76aa9 Reviewed-on: https://review.haiku-os.org/c/buildtools/+/3020 Reviewed-by: Adrien Destugues <pulkomandy@gmail.com>
205 lines
4.6 KiB
C
205 lines
4.6 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-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 either:
|
|
|
|
* 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.
|
|
|
|
or
|
|
|
|
* the GNU General Public License as published by the Free Software
|
|
Foundation; either version 2 of the License, or (at your option) any
|
|
later version.
|
|
|
|
or both in parallel, as here.
|
|
|
|
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 General Public License
|
|
for more details.
|
|
|
|
You should have received copies of the GNU General Public License and the
|
|
GNU Lesser General Public License along with the GNU MP Library. If not,
|
|
see https://www.gnu.org/licenses/. */
|
|
|
|
#include <stdlib.h> /* for NULL */
|
|
|
|
#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;
|
|
}
|