mpz_sqrt_if_perfect_square

Niels Möller nisse at lysator.liu.se
Fri Jun 5 20:08:06 CEST 2026


Seth Troisi <braintwo at gmail.com> writes:

> New patch using mpz_perfect_square_root name
> + at deftypefun int mpn_perfect_square_trivial_p (const mp_limb_t *@var{s1p}, mp_size_t @var{n})
> +Return non-zero iff @{@var{s1p}, @var{n}@} passes preliminary perfect square checks.
> +The most significant limb of the input @{@var{s1p}, @var{n}@} must be non-zero.
>  @end deftypefun

Another function that needs the right name... I don't think "trivial" is
the right attribute. Maybe mpn_probably_perfect_square_p (even though
it's not really probabilistic) for consistency with probably_prime_p. We
need something to convey that it's fast and cheap (and not totally
accurate). Regardless of name, maybe say clarly that if it returns
false, the input is certainly not a square.

>  @deftypefun void mpn_and_n (mp_limb_t *@var{rp}, const mp_limb_t *@var{s1p}, const mp_limb_t *@var{s2p}, mp_size_t @var{n})
> @@ -9250,9 +9263,10 @@
>  A significant fraction of non-squares can be quickly identified by checking
>  whether the input is a quadratic residue modulo small integers.
>  
> - at code{mpz_perfect_square_p} first tests the input mod 256, which means just
> -examining the low byte.  Only 44 different values occur for squares mod 256,
> -so 82.8% of inputs can be immediately identified as non-squares.
> + at code{mpz_perfect_square_p}/@code{mpz_perfect_square_root} first tests the input
> +mod 256, which means just examining the low byte.  Only 44 different values
> +occur for squares mod 256, so 82.8% of inputs can be immediately identified
> +as non-squares.

I think these docs should be attached to mpn_perfect_square_trivial_p.

> +int
> +mpz_perfect_square_root (mpz_ptr rop, mpz_srcptr u)
> +{
> +  mp_size_t u_size = SIZ (u);
> +
> +  /* Handle <= 0 */
> +  if (__GMP_UNLIKELY ( u_size <= 0))
> +    {
> +      /* Zero is a square with rop = 0 */
> +      if (__GMP_UNLIKELY ( u_size == 0))

Not sure this inner  __GMP_UNLIKELY is helpful, I'd leave to the branch
predictor to find out if zero or negative is more common.

> +        SIZ(rop) = 0;
> +        return 1;
> +      }
> +      /* Negatives are never perfect squares. */
> +      return 0;
> +    }
> +
> +  if (! mpn_perfect_square_trivial_p (PTR (u), u_size))
> +    return 0;
> +
> +  int rop_size = (SIZ (u) + 1) / 2;

Clearer with mp_size_t instead of int. And I think it may be worth a comment
saying that this size is precise (limb rop_size - 1 will always be non-zero).

> +  /* mpn_sqrtrem doesn't allow sp == np */
> +  if (rop == u)
> +    {
> +      TMP_DECL;
> +      TMP_MARK;
> +
> +      mp_ptr rop_ptr = TMP_ALLOC_LIMBS (rop_size);
> +      int res = ! mpn_sqrtrem (rop_ptr, NULL, PTR (u), u_size);
> +
> +      /* It might be better to always copy so the value in rop
> +         is more determistic. */

Unless I'm missing something, the current code leaves rop unchanged when
res == 0? That seems deterministic enough.

> +      if (res)
> +        {
> +          MPN_COPY (PTR(rop), rop_ptr, rop_size);
> +          SIZ(rop) = rop_size;
> +        }
> +
> +      TMP_FREE;
> +      return res;
> +    }
> +
> +  /* Make sure rop has space for the square root. */
> +  MPZ_REALLOC(rop, rop_size);
> +
> +  int res = ! mpn_sqrtrem (PTR (rop), NULL, PTR (u), u_size);

I think MPZ_REALLOC returns the pointer, so you could write this as 

     int res = ! mpn_sqrtrem (MPZ_REALLOC (rop, rop_size), NULL, PTR (u), u_size);

Or do you think it is clearer with MPZ_REALLOC more separated?

> +  if (res)
> +    {
> +      SIZ(rop) = rop_size;
> +    }
> +  return res;

In the non-square case, I think there's a risk that we produce a rop
that isn't normalized. To keep things simple, maybe just set SIZ(rop) =
0 in that case? I.e.,

  SIZ (rop) = res ? rop_size : 0;
  return res;

Regards,
/Niels

-- 
Niels Möller. PGP key CB4962D070D77D7FCB8BA36271D8F1FF368C6677.
Internet email is subject to wholesale government surveillance.


More information about the gmp-devel mailing list