Re: mini-gmp mpz_gcdext Bézout coefficients do not match documentation

2024-02-18 Thread Niels Möller
marco.bodr...@tutanota.com writes:

> The only reason why I prefer my solution is: when cmp<0, there is no need to 
> compute
> mpz_sub (t1, t0, t1);

That's certainly a micro optimization, but let's keep things simple.

I've pushed this fix now, I think there were only comment and ChangeLog
changes since the previous version of the patch.

Thanks for the review,
/Niels

-- 
Niels Möller. PGP key CB4962D070D77D7FCB8BA36271D8F1FF368C6677.
Internet email is subject to wholesale government surveillance.
___
gmp-bugs mailing list
gmp-bugs@gmplib.org
https://gmplib.org/mailman/listinfo/gmp-bugs


Re: major formatted output function bug with %c and the value 0

2024-02-18 Thread marco . bodrato
Ciao Vincent,

15 dic 2023, 13:26 da vinc...@vinc17.net:

> Note that there are similar issues in printf/repl-vsnprintf.c, and I
>

I finally had the time to examine the code and test it. I attach a proposed 
patch.

I changed the 3 files: printf/doprntf.c, printf/repl-vsnprintf.c, 
printf/sprintffuns.c, to use the returned value of the called functions, 
instead of calling strlen on the string.

I also slightly changed the tests, to check at least one case with "%c", 0.
For this to work I also had to change a
-  (*__gmp_free_func) (got, strlen(got)+1);
+  (*__gmp_free_func) (got, got_len+1);
When the string comes from a generic print, it is safer to relay on the return 
value than on the length computed by strlen()...


> think that this is the cause of the MPFR failure with MS Visual C++
> (vsnprintf is used by gmp_vasprintf).
>
If you can also test on that side, let me know. Thanks!

Gis,
m

diff -r 1c566525476a printf/doprntf.c
--- a/printf/doprntf.c	Wed Dec 27 19:35:36 2023 +0100
+++ b/printf/doprntf.c	Sun Feb 18 12:17:18 2024 +0100
@@ -267,8 +267,7 @@
 	 mean truncation */
   ASSERT (explen >= 0 && explen < sizeof(exponent)-1);
 #else
-  sprintf (exponent, p->expfmt, expsign, expval);
-  explen = strlen (exponent);
+  explen = sprintf (exponent, p->expfmt, expsign, expval);
   ASSERT (explen < sizeof(exponent));
 #endif
   TRACE (printf ("  expfmt %s gives %s\n", p->expfmt, exponent));
diff -r 1c566525476a printf/repl-vsnprintf.c
--- a/printf/repl-vsnprintf.c	Wed Dec 27 19:35:36 2023 +0100
+++ b/printf/repl-vsnprintf.c	Sun Feb 18 12:17:18 2024 +0100
@@ -364,16 +364,14 @@
 
   if (total_width <= buf_size)
 {
-  vsprintf (buf, orig_fmt, orig_ap);
-  len = strlen (buf);
+  len = vsprintf (buf, orig_fmt, orig_ap);
 }
   else
 {
   char  *s;
 
   s = __GMP_ALLOCATE_FUNC_TYPE (total_width, char);
-  vsprintf (s, orig_fmt, orig_ap);
-  len = strlen (s);
+  len = vsprintf (s, orig_fmt, orig_ap);
   if (buf_size != 0)
 	{
 	  size_t  copylen = MIN (len, buf_size-1);
diff -r 1c566525476a printf/sprintffuns.c
--- a/printf/sprintffuns.c	Wed Dec 27 19:35:36 2023 +0100
+++ b/printf/sprintffuns.c	Sun Feb 18 12:17:18 2024 +0100
@@ -53,9 +53,9 @@
 {
   char  *buf = *bufp;
   int   ret;
-  vsprintf (buf, fmt, ap);
-  ret = strlen (buf);
-  *bufp = buf + ret;
+  ret = vsprintf (buf, fmt, ap);
+  if (ret > 0)
+*bufp = buf + ret;
   return ret;
 }
 
diff -r 1c566525476a tests/misc/t-printf.c
--- a/tests/misc/t-printf.c	Wed Dec 27 19:35:36 2023 +0100
+++ b/tests/misc/t-printf.c	Sun Feb 18 12:17:18 2024 +0100
@@ -128,12 +128,11 @@
 }
 
 void
-check_vsprintf (const char *want, const char *fmt, va_list ap)
+check_vsprintf (const char *want, int want_len, const char *fmt, va_list ap)
 {
   char  got[MAX_OUTPUT];
-  int   got_len, want_len;
+  int   got_len;
 
-  want_len = strlen (want);
   got_len = gmp_vsprintf (got, fmt, ap);
 
   if (got_len != want_len || strcmp (got, want) != 0)
@@ -149,14 +148,12 @@
 }
 
 void
-check_vfprintf (const char *want, const char *fmt, va_list ap)
+check_vfprintf (const char *want, int want_len, const char *fmt, va_list ap)
 {
   char  got[MAX_OUTPUT];
-  int   got_len, want_len, fread_len;
+  int   got_len, fread_len;
   long  ftell_len;
 
-  want_len = strlen (want);
-
   rewind (check_vfprintf_fp);
   got_len = gmp_vfprintf (check_vfprintf_fp, fmt, ap);
   ASSERT_ALWAYS (got_len != -1);
@@ -187,14 +184,12 @@
 }
 
 void
-check_vsnprintf (const char *want, const char *fmt, va_list ap)
+check_vsnprintf (const char *want, int want_len, const char *fmt, va_list ap)
 {
   chargot[MAX_OUTPUT+1];
-  int ret, got_len, want_len;
+  int ret, got_len;
   size_t  bufsize;
 
-  want_len = strlen (want);
-
   bufsize = -1;
   for (;;)
 {
@@ -243,12 +238,11 @@
 }
 
 void
-check_vasprintf (const char *want, const char *fmt, va_list ap)
+check_vasprintf (const char *want, int want_len, const char *fmt, va_list ap)
 {
   char  *got;
-  int   got_len, want_len;
+  int   got_len;
 
-  want_len = strlen (want);
   got_len = gmp_vasprintf (, fmt, ap);
 
   if (got_len != want_len || strcmp (got, want) != 0)
@@ -261,19 +255,17 @@
   printf ("  want_len %d\n", want_len);
   abort ();
 }
-  (*__gmp_free_func) (got, strlen(got)+1);
+  (*__gmp_free_func) (got, got_len+1);
 }
 
 void
-check_obstack_vprintf (const char *want, const char *fmt, va_list ap)
+check_obstack_vprintf (const char *want, int want_len, const char *fmt, va_list ap)
 {
 #if HAVE_OBSTACK_VPRINTF
   struct obstack  ob;
-  int   got_len, want_len, ob_len;
+  int   got_len, ob_len;
   char  *got;
 
-  want_len = strlen (want);
-
   obstack_init ();
   got_len = gmp_obstack_vprintf (, fmt, ap);
   got = (char *) obstack_base ();
@@ -300,15 +292,30 @@
 void
 check_one (const char *want, const char *fmt, ...)
 {
+  int want_len = strlen (want);
   va_list ap;
   va_start (ap, fmt);
 
   /* simplest first */
-  check_vsprintf (want, fmt, ap);
-  

Re: mini-gmp mpz_gcdext Bézout coefficients do not match documentation

2024-02-18 Thread marco . bodrato
Ciao Niels,

18 feb 2024, 10:10 da ni...@lysator.liu.se:

> marco.bodr...@tutanota.com writes:
>
>> What about;
>>
>>
>> /* Arrange so that |s| < |u| / 2g */
>>   mpz_add (s1, s0, s1);
>>   {
>>     int cmp = mpz_cmpabs (s0, s1);
>>     if (cmp >= 0)
>>   {
>>     mpz_swap (s0, s1);
>>     mpz_sub (t1, t0, t1);
>>     if (cmp != 0 || mpz_cmpabs (t0, t1) > 0)
>>   mpz_swap (t0, t1);
>>   }
>>   }
>>
>
> I think swapping of s and t must go together? I've tried out this
> similar variant 
>

You are right, it's better with the two swaps together.


>  mpz_add (s1, s0, s1);
>  mpz_sub (t1, t0, t1);
>  cmp = mpz_cmpabs (s0, s1);
>  if (cmp > 0 || (cmp == 0 && mpz_cmpabs (t0, t1) > 0))
>  {
>  mpz_swap (s0, s1);
>  mpz_swap (t0, t1);
>  }
>

The only reason why I prefer my solution is: when cmp<0, there is no need to 
compute
mpz_sub (t1, t0, t1);


> Seems to work fine, and it's a rather clear way to express the "lexical"
> preference that we want the cofactor pair with the smallest of |s|, |s'|, but
> in case of a tie, we choose the pair with smallest |t|, |t'|.
>

I agree.

Ĝis,
m
___
gmp-bugs mailing list
gmp-bugs@gmplib.org
https://gmplib.org/mailman/listinfo/gmp-bugs


Re: mini-gmp mpz_gcdext Bézout coefficients do not match documentation

2024-02-18 Thread Niels Möller
marco.bodr...@tutanota.com writes:

> s==0 is not a special case, it's one of the cases with smaller |s|.

Right, s == 0 is not a special case in itself, what's special is the
case g = a = b, then we have too candidate cofactor pairs, s=1, t=0,
s'=0, t'= 1. And we have the asymmetric preference for the first.

> What about;
>
> /* Arrange so that |s| < |u| / 2g */
>   mpz_add (s1, s0, s1);
>   {
>     int cmp = mpz_cmpabs (s0, s1);
>     if (cmp >= 0)
>   {
>     mpz_swap (s0, s1);
>     mpz_sub (t1, t0, t1);
>     if (cmp != 0 || mpz_cmpabs (t0, t1) > 0)
>   mpz_swap (t0, t1);
>   }
>   }

I think swapping of s and t must go together? I've tried out this
similar variant 

  mpz_add (s1, s0, s1);
  mpz_sub (t1, t0, t1);
  cmp = mpz_cmpabs (s0, s1);
  if (cmp > 0 || (cmp == 0 && mpz_cmpabs (t0, t1) > 0))
{
  mpz_swap (s0, s1);
  mpz_swap (t0, t1);
}

Seems to work fine, and it's a rather clear way to express the "lexical"
preference that we want the cofactor pair with the smallest of |s|, |s'|, but
in case of a tie, we choose the pair with smallest |t|, |t'|.

Updated version of the patch appended below.

Regrads,
/Niels

diff -r 1c566525476a mini-gmp/mini-gmp.c
--- a/mini-gmp/mini-gmp.c   Wed Dec 27 19:35:36 2023 +0100
+++ b/mini-gmp/mini-gmp.c   Sun Feb 18 10:02:56 2024 +0100
@@ -2809,6 +2809,7 @@
   mpz_t tu, tv, s0, s1, t0, t1;
   mp_bitcnt_t uz, vz, gz;
   mp_bitcnt_t power;
+  int cmp;
 
   if (u->_mp_size == 0)
 {
@@ -2960,12 +2961,21 @@
   mpz_tdiv_q_2exp (t0, t0, 1);
 }
 
-  /* Arrange so that |s| < |u| / 2g */
+  /* Choose small cofactors (they should satify
+
+   |s| < |u| / 2g and |t| < |v| / 2g,
+
+ except for documented exceptions). Always choose the smallest s,
+ if there are two choices for s with same absolute value, choose
+ the one with smallest corresponding t (this asymmetryic condition
+ is needed to prefer s = 0, |t| = 1 when g = |a| = |b|). */
   mpz_add (s1, s0, s1);
-  if (mpz_cmpabs (s0, s1) > 0)
+  mpz_sub (t1, t0, t1);
+  cmp = mpz_cmpabs (s0, s1);
+  if (cmp > 0 || (cmp == 0 && mpz_cmpabs (t0, t1) > 0))
 {
   mpz_swap (s0, s1);
-  mpz_sub (t0, t0, t1);
+  mpz_swap (t0, t1);
 }
   if (u->_mp_size < 0)
 mpz_neg (s0, s0);
diff -r 1c566525476a mini-gmp/tests/t-gcd.c
--- a/mini-gmp/tests/t-gcd.cWed Dec 27 19:35:36 2023 +0100
+++ b/mini-gmp/tests/t-gcd.cSun Feb 18 10:02:56 2024 +0100
@@ -55,6 +55,10 @@
   if (mpz_sgn (g) <= 0)
 return 0;
 
+  /* Require that s==0 iff g==abs(b) */
+  if (!mpz_sgn (s) != !mpz_cmpabs (g, b))
+goto fail;
+
   mpz_init (ta);
   mpz_init (tb);
   mpz_init (r);
@@ -79,20 +83,20 @@
   if (mpz_sgn (r) != 0)
 goto fail;
 
-  /* Require that 2 |s| < |b/g|, or |s| == 1. */
-  if (mpz_cmpabs_ui (s, 1) > 0)
+  /* Require that 2 |s| < |b/g|, or s == sgn(a) */
+  if (mpz_cmp_si (s, mpz_sgn (a)) != 0)
 {
   mpz_mul_2exp (r, s, 1);
-  if (mpz_cmpabs (r, tb) > 0)
+  if (mpz_cmpabs (r, tb) >= 0)
goto fail;
 }
 
-  /* Require that 2 |t| < |a/g| or |t| == 1*/
-  if (mpz_cmpabs_ui (t, 1) > 0)
+  /* Require that 2 |t| < |a/g| or t == sgn(b) */
+  if (mpz_cmp_si (t, mpz_sgn (b)) != 0)
 {
   mpz_mul_2exp (r, t, 1);
-  if (mpz_cmpabs (r, ta) > 0)
-   return 0;
+  if (mpz_cmpabs (r, ta) >= 0)
+   goto fail;
 }
 
   mpz_clear (ta);
@@ -102,17 +106,53 @@
   return 1;
 }
 
+static void test_one(const mpz_t a, const mpz_t b)
+{
+  mpz_t g, s, t;
+
+  mpz_init (g);
+  mpz_init (s);
+  mpz_init (t);
+
+  mpz_gcdext (g, s, t, a, b);
+  if (!gcdext_valid_p (a, b, g, s, t))
+{
+  fprintf (stderr, "mpz_gcdext failed:\n");
+  dump ("a", a);
+  dump ("b", b);
+  dump ("g", g);
+  dump ("s", s);
+  dump ("t", t);
+  abort ();
+}
+
+  mpz_gcd (s, a, b);
+  if (mpz_cmp (g, s))
+{
+  fprintf (stderr, "mpz_gcd failed:\n");
+  dump ("a", a);
+  dump ("b", b);
+  dump ("r", g);
+  dump ("ref", s);
+  abort ();
+}
+
+  mpz_clear (g);
+  mpz_clear (s);
+  mpz_clear (t);
+}
+
 void
 testmain (int argc, char **argv)
 {
   unsigned i;
-  mpz_t a, b, g, s, t;
+  mpz_t a, b, g, s;
+  int ai, bi;
 
   mpz_init (a);
   mpz_init (b);
   mpz_init (g);
   mpz_init (s);
-  mpz_init (t);
 
   for (i = 0; i < COUNT; i++)
 {
@@ -129,6 +169,15 @@
}
 }
 
+  /* Exhaustive test of small inputs */
+  for (ai = -30; ai <= 30; ai++)
+for (bi = -30; bi <= 30; bi++)
+  {
+   mpz_set_si (a, ai);
+   mpz_set_si (b, bi);
+   test_one (a, b);
+  }
+
   for (i = 0; i < COUNT; i++)
 {
   unsigned flags;
@@ -147,32 +196,11 @@
   if (flags & 2)
mpz_neg (b, b);
 
-  mpz_gcdext (g, s, t, a, b);
-  if (!gcdext_valid_p (a, b, g, s, t))
-   {
- fprintf (stderr, "mpz_gcdext failed:\n");
- dump ("a", a);
- dump ("b", b);
- dump ("g", g);
- dump ("s", s);
- dump ("t",