1/* Test file for mpfr_set_ld and mpfr_get_ld. 2 3Copyright 2002-2023 Free Software Foundation, Inc. 4Contributed by the AriC and Caramba projects, INRIA. 5 6This file is part of the GNU MPFR Library. 7 8The GNU MPFR Library is free software; you can redistribute it and/or modify 9it under the terms of the GNU Lesser General Public License as published by 10the Free Software Foundation; either version 3 of the License, or (at your 11option) any later version. 12 13The GNU MPFR Library is distributed in the hope that it will be useful, but 14WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 15or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 16License for more details. 17 18You should have received a copy of the GNU Lesser General Public License 19along with the GNU MPFR Library; see the file COPYING.LESSER. If not, see 20https://www.gnu.org/licenses/ or write to the Free Software Foundation, Inc., 2151 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. */ 22 23#include <float.h> 24 25#include "mpfr-test.h" 26 27static void 28check_gcc33_bug (void) 29{ 30 volatile long double x; 31 32 x = (long double) 9007199254740992.0 + 1.0; 33 if (x != 0.0) 34 return; /* OK */ 35 printf 36 ("Detected optimization bug of gcc 3.3 on Alpha concerning long double\n" 37 "comparisons; set_ld tests might fail (set_ld won't work correctly).\n" 38 "See https://gcc.gnu.org/legacy-ml/gcc-bugs/2003-10/msg00853.html for\n" 39 "more information.\n"); 40} 41 42static int 43Isnan_ld (long double d) 44{ 45 /* Do not convert d to double as this can give an overflow, which 46 may confuse compilers without IEEE 754 support (such as clang 47 -fsanitize=undefined), or trigger a trap if enabled. 48 The DOUBLE_ISNAN macro should work fine on long double. */ 49 if (DOUBLE_ISNAN (d)) 50 return 1; 51 LONGDOUBLE_NAN_ACTION (d, goto yes); 52 return 0; 53 yes: 54 return 1; 55} 56 57/* Return the minimal number of bits to represent d exactly (0 for zero). 58 If flag is non-zero, also print d. */ 59/* FIXME: This function doesn't work if the rounding precision is reduced. */ 60static mpfr_prec_t 61print_binary (long double d, int flag) 62{ 63 long double e, f, r; 64 long exp = 1; 65 mpfr_prec_t prec = 0; 66 67 if (Isnan_ld (d)) 68 { 69 if (flag) 70 printf ("NaN\n"); 71 return 0; 72 } 73 if (d < (long double) 0.0 74#if !defined(MPFR_ERRDIVZERO) 75 || (d == (long double) 0.0 && (1.0 / (double) d < 0.0)) 76#endif 77 ) 78 { 79 if (flag) 80 printf ("-"); 81 d = -d; 82 } 83 /* now d >= 0 */ 84 /* Use 2 different tests for Inf, to avoid potential bugs 85 in implementations. */ 86 if (Isnan_ld (d - d) || (d > 1 && d * 0.5 == d)) 87 { 88 if (flag) 89 printf ("Inf\n"); 90 return 0; 91 } 92 if (d == (long double) 0.0) 93 { 94 if (flag) 95 printf ("0.0\n"); 96 return prec; 97 } 98 MPFR_ASSERTN (d > 0); 99 e = (long double) 1.0; 100 while (e > d) 101 { 102 e = e * (long double) 0.5; 103 exp --; 104 } 105 if (flag == 2) printf ("1: e=%.36Le\n", e); 106 MPFR_ASSERTN (d >= e); 107 /* FIXME: There can be an overflow here, which may not be supported 108 on all platforms. */ 109 while (f = e + e, d >= f) 110 { 111 e = f; 112 exp ++; 113 } 114 if (flag == 2) printf ("2: e=%.36Le\n", e); 115 MPFR_ASSERTN (e <= d && d < f); 116 if (flag == 1) 117 printf ("0."); 118 if (flag == 2) printf ("3: d=%.36Le e=%.36Le prec=%ld\n", d, e, 119 (long) prec); 120 /* Note: the method we use here to extract the bits of d is the following, 121 to deal with the case where the rounding precision is less than the 122 precision of d: 123 (1) we accumulate the upper bits of d into f 124 (2) when accumulating a new bit into f is not exact, we subtract 125 f from d and reset f to 0 126 This is guaranteed to work only when the rounding precision is at least 127 half the precision of d, since otherwise d-f might not be exact. 128 This method does not work with flush-to-zero on underflow. */ 129 f = 0.0; /* will hold accumulated powers of 2 */ 130 while (1) 131 { 132 prec++; 133 r = f + e; 134 /* r is close to f (in particular in the cases where f+e may 135 not be exact), so that r - f should be exact. */ 136 if (r - f != e) /* f+e is not exact */ 137 { 138 d -= f; /* should be exact */ 139 f = 0.0; 140 r = e; 141 } 142 if (d >= r) 143 { 144 if (flag == 1) 145 printf ("1"); 146 if (d == r) 147 break; 148 f = r; 149 } 150 else 151 { 152 if (flag == 1) 153 printf ("0"); 154 } 155 e *= (long double) 0.5; 156 MPFR_ASSERTN (e != 0); /* may fail with flush-to-zero on underflow */ 157 if (flag == 2) printf ("4: d=%.36Le e=%.36Le prec=%ld\n", d, e, 158 (long) prec); 159 } 160 if (flag == 1) 161 printf ("e%ld\n", exp); 162 return prec; 163} 164 165/* Checks that a long double converted exactly to a MPFR number, then 166 converted back to a long double gives the initial value, or in other 167 words, mpfr_get_ld(mpfr_set_ld(d)) = d. 168*/ 169static void 170check_set_get (long double d) 171{ 172 mpfr_exp_t emin, emax; 173 mpfr_t x; 174 mpfr_prec_t prec; 175 int r; 176 long double e; 177 int inex; 178 int red; 179 180 emin = mpfr_get_emin (); 181 emax = mpfr_get_emax (); 182 183 /* Select a precision to ensure that the conversion of d to x be exact. */ 184 prec = print_binary (d, 0); 185 if (prec < MPFR_PREC_MIN) 186 prec = MPFR_PREC_MIN; 187 mpfr_init2 (x, prec); 188 189 RND_LOOP(r) 190 { 191 inex = mpfr_set_ld (x, d, (mpfr_rnd_t) r); 192 if (inex != 0) 193 { 194 printf ("Error: mpfr_set_ld should be exact (rnd = %s)\n", 195 mpfr_print_rnd_mode ((mpfr_rnd_t) r)); 196 /* We use 36 digits here, as the maximum LDBL_MANT_DIG value 197 seen in the current implementations is 113 (binary128), 198 and ceil(1+113*log(2)/log(10)) = 36. But the current glibc 199 implementation of printf with double-double arithmetic 200 (e.g. on PowerPC) is not accurate. */ 201 printf (" d ~= %.36Le (output may be wrong!)\n", d); 202 printf (" inex = %d\n", inex); 203 if (emin >= LONG_MIN) 204 printf (" emin = %ld\n", (long) emin); 205 if (emax <= LONG_MAX) 206 printf (" emax = %ld\n", (long) emax); 207 ld_trace (" d", d); 208 printf (" d = "); 209 print_binary (d, 1); 210 printf (" x = "); 211 mpfr_dump (x); 212 printf (" MPFR_LDBL_MANT_DIG=%u\n", MPFR_LDBL_MANT_DIG); 213 printf (" prec=%ld\n", (long) prec); 214 print_binary (d, 2); 215 exit (1); 216 } 217 for (red = 0; red < 2; red++) 218 { 219 if (red) 220 { 221 mpfr_exp_t ex; 222 223 if (MPFR_IS_SINGULAR (x)) 224 break; 225 ex = MPFR_GET_EXP (x); 226 set_emin (ex); 227 set_emax (ex); 228 } 229 e = mpfr_get_ld (x, (mpfr_rnd_t) r); 230 set_emin (emin); 231 set_emax (emax); 232 if (inex == 0 && ((Isnan_ld(d) && ! Isnan_ld(e)) || 233 (Isnan_ld(e) && ! Isnan_ld(d)) || 234 (e != d && !(Isnan_ld(e) && Isnan_ld(d))))) 235 { 236 printf ("Error: mpfr_get_ld o mpfr_set_ld <> Id%s\n", 237 red ? ", reduced exponent range" : ""); 238 printf (" rnd = %s\n", mpfr_print_rnd_mode ((mpfr_rnd_t) r)); 239 printf (" d ~= %.36Le (output may be wrong!)\n", d); 240 printf (" e ~= %.36Le (output may be wrong!)\n", e); 241 ld_trace (" d", d); 242 printf (" x = "); 243 mpfr_out_str (stdout, 16, 0, x, MPFR_RNDN); 244 printf ("\n"); 245 ld_trace (" e", e); 246 printf (" d = "); 247 print_binary (d, 1); 248 printf (" x = "); 249 mpfr_dump (x); 250 printf (" e = "); 251 print_binary (e, 1); 252 printf (" MPFR_LDBL_MANT_DIG=%u\n", MPFR_LDBL_MANT_DIG); 253#ifdef MPFR_NANISNAN 254 if (Isnan_ld(d) || Isnan_ld(e)) 255 printf ("The reason is that NAN == NAN. Please look at the " 256 "configure output\nand Section \"In case of problem\"" 257 " of the INSTALL file.\n"); 258#endif 259 exit (1); 260 } 261 } 262 } 263 264 mpfr_clear (x); 265} 266 267static void 268test_small (void) 269{ 270 mpfr_t x, y, z; 271 long double d; 272 273 mpfr_init2 (x, MPFR_LDBL_MANT_DIG); 274 mpfr_init2 (y, MPFR_LDBL_MANT_DIG); 275 mpfr_init2 (z, MPFR_LDBL_MANT_DIG); 276 277 /* x = 11906603631607553907/2^(16381+64) */ 278 mpfr_set_str (x, "0.1010010100111100110000001110101101000111010110000001111101110011E-16381", 2, MPFR_RNDN); 279 d = mpfr_get_ld (x, MPFR_RNDN); /* infinite loop? */ 280 mpfr_set_ld (y, d, MPFR_RNDN); 281 mpfr_sub (z, x, y, MPFR_RNDN); 282 mpfr_abs (z, z, MPFR_RNDN); 283 mpfr_clear_erangeflag (); 284 /* If long double = double, d should be equal to 0; 285 in this case, everything is OK. */ 286 if (d != 0 && (mpfr_cmp_str (z, "1E-16434", 2, MPFR_RNDN) > 0 || 287 mpfr_erangeflag_p ())) 288 { 289 printf ("Error with x = "); 290 mpfr_out_str (stdout, 10, 21, x, MPFR_RNDN); 291 printf (" = "); 292 mpfr_out_str (stdout, 16, 0, x, MPFR_RNDN); 293 printf ("\n -> d = %.33Le", d); 294 printf ("\n -> y = "); 295 mpfr_out_str (stdout, 10, 21, y, MPFR_RNDN); 296 printf (" = "); 297 mpfr_out_str (stdout, 16, 0, y, MPFR_RNDN); 298 printf ("\n -> |x-y| = "); 299 mpfr_out_str (stdout, 16, 0, z, MPFR_RNDN); 300 printf ("\n"); 301 exit (1); 302 } 303 304 mpfr_clear (x); 305 mpfr_clear (y); 306 mpfr_clear (z); 307} 308 309static void 310test_fixed_bugs (void) 311{ 312 mpfr_t x; 313 long double l, m; 314 315 /* bug found by Steve Kargl (2009-03-14) */ 316 mpfr_init2 (x, MPFR_LDBL_MANT_DIG); 317 mpfr_set_ui_2exp (x, 1, -16447, MPFR_RNDN); 318 mpfr_get_ld (x, MPFR_RNDN); /* an assertion failed in init2.c:50 */ 319 320 /* bug reported by Jakub Jelinek (2010-10-17) 321 https://gforge.inria.fr/tracker/?func=detail&aid=11300 */ 322 mpfr_set_prec (x, MPFR_LDBL_MANT_DIG); 323 /* l = 0x1.23456789abcdef0123456789abcdp-914L; */ 324 l = 8.215640181713713164092636634579e-276; 325 mpfr_set_ld (x, l, MPFR_RNDN); 326 m = mpfr_get_ld (x, MPFR_RNDN); 327 if (m != l) 328 { 329 printf ("Error in get_ld o set_ld for l=%Le\n", l); 330 printf ("Got m=%Le instead of l\n", m); 331 exit (1); 332 } 333 334 /* another similar test which failed with extended double precision and the 335 generic code for mpfr_set_ld */ 336 /* l = 0x1.23456789abcdef0123456789abcdp-968L; */ 337 l = 4.560596445887084662336528403703e-292; 338 mpfr_set_ld (x, l, MPFR_RNDN); 339 m = mpfr_get_ld (x, MPFR_RNDN); 340 if (m != l) 341 { 342 printf ("Error in get_ld o set_ld for l=%Le\n", l); 343 printf ("Got m=%Le instead of l\n", m); 344 exit (1); 345 } 346 347 mpfr_clear (x); 348} 349 350static void 351check_subnormal (void) 352{ 353 long double d, e; 354 mpfr_t x; 355 356 d = 17.0; 357 mpfr_init2 (x, MPFR_LDBL_MANT_DIG); 358 while (d != 0.0) 359 { 360 mpfr_set_ld (x, d, MPFR_RNDN); 361 e = mpfr_get_ld (x, MPFR_RNDN); 362 if (e != d) 363 { 364 printf ("Error in check_subnormal for mpfr_get_ld o mpfr_set_ld\n"); 365 printf ("d=%.30Le\n", d); 366 printf ("x="); mpfr_dump (x); 367 printf ("e=%.30Le\n", e); 368 exit (1); 369 } 370 d *= 0.5; 371 } 372 mpfr_clear (x); 373} 374 375static void 376check_overflow (void) 377{ 378 long double d, e; 379 mpfr_t x; 380 int i; 381 382 mpfr_init2 (x, MPFR_LDBL_MANT_DIG); 383 for (i = 0; i < 2; i++) 384 { 385 d = i == 0 ? LDBL_MAX : -LDBL_MAX; 386 mpfr_set_ld (x, d, MPFR_RNDN); 387 mpfr_mul_2ui (x, x, 1, MPFR_RNDN); 388 e = mpfr_get_ld (x, MPFR_RNDN); 389 if (! DOUBLE_ISINF (e) || (i == 0 ? (e < 0) : (e > 0))) 390 { 391 printf ("Error in check_overflow.\n"); 392 printf ("d=%Le\n", d); 393 printf ("x="); mpfr_dump (x); 394 printf ("e=%Le\n", e); 395 exit (1); 396 } 397 } 398 mpfr_clear (x); 399} 400 401/* issue reported by Sisyphus on powerpc */ 402static void 403test_20140212 (void) 404{ 405 mpfr_t fr1, fr2; 406 long double ld, h, l, ld2; 407 int i, c1, c2; 408 409 mpfr_init2 (fr1, 106); 410 mpfr_init2 (fr2, 2098); 411 412 for (h = 1.0L, i = 0; i < 1023; i++) 413 h *= 2.0L; 414 for (l = 1.0L, i = 0; i < 1074; i++) 415 l *= 0.5L; 416 ld = h + l; /* rounding of 2^1023 + 2^(-1074) */ 417 418 mpfr_set_ld (fr1, ld, MPFR_RNDN); 419 mpfr_set_ld (fr2, ld, MPFR_RNDN); 420 421 c1 = mpfr_cmp_ld (fr1, ld); 422 c2 = mpfr_cmp_ld (fr2, ld); 423 424 /* If long double is binary64, then ld = fr1 = fr2 = 2^1023. 425 If long double is double-double, then ld = 2^1023 + 2^(-1074), 426 fr1 = 2^1023 and fr2 = 2^1023 + 2^(-1074) */ 427 MPFR_ASSERTN(ld == h ? (c1 == 0) : (c1 < 0)); 428 429 MPFR_ASSERTN(c2 == 0); 430 431 ld2 = mpfr_get_ld (fr2, MPFR_RNDN); 432 MPFR_ASSERTN(ld2 == ld); 433 434 mpfr_clear (fr1); 435 mpfr_clear (fr2); 436} 437 438/* bug reported by Walter Mascarenhas 439 https://sympa.inria.fr/sympa/arc/mpfr/2016-09/msg00005.html */ 440static void 441bug_20160907 (void) 442{ 443#if HAVE_LDOUBLE_IEEE_EXT_LITTLE 444 long double dn, ld; 445 mpfr_t mp; 446 long e; 447 mpfr_long_double_t x; 448 449 /* the following is the encoding of the smallest subnormal number 450 for HAVE_LDOUBLE_IEEE_EXT_LITTLE */ 451 x.s.manl = 1; 452 x.s.manh = 0; 453 x.s.expl = 0; 454 x.s.exph = 0; 455 x.s.sign= 0; 456 dn = x.ld; 457 e = -16445; 458 /* dn=2^e is now the smallest subnormal. */ 459 460 mpfr_init2 (mp, 64); 461 mpfr_set_ui_2exp (mp, 1, e - 1, MPFR_RNDN); 462 ld = mpfr_get_ld (mp, MPFR_RNDU); 463 /* since mp = 2^(e-1) and ld is rounded upwards, we should have 464 ld = 2^e */ 465 if (ld != dn) 466 { 467 printf ("Error, ld = %Le <> dn = %Le\n", ld, dn); 468 printf ("mp="); 469 mpfr_out_str (stdout, 10, 0, mp, MPFR_RNDN); 470 printf ("\n"); 471 printf ("mp="); mpfr_dump (mp); 472 exit (1); 473 } 474 475 /* check a few more numbers */ 476 for (e = -16446; e <= -16381; e++) 477 { 478 mpfr_set_ui_2exp (mp, 1, e, MPFR_RNDN); 479 ld = mpfr_get_ld (mp, MPFR_RNDU); 480 mpfr_set_ld (mp, ld, MPFR_RNDU); 481 /* mp is 2^e rounded up, thus should be >= 2^e */ 482 if (mpfr_cmp_ui_2exp (mp, 1, e) < 0) 483 { 484 if (tests_run_within_valgrind () && MPFR_IS_ZERO (mp)) 485 { 486 /* Since this is not a bug in MPFR and it is just caused by 487 Valgrind, let's output a message and skip the remaining 488 part of the test without an error. Note that the message 489 will be not be visible via "make check". 490 Note that the other tests do not fail probably because 491 long double has the same behavior as double (which is 492 allowed by the C standard), but here this is a test that 493 is specific to x86 extended precision. */ 494 printf 495 ("Error in bug_20160907 due to a bug in Valgrind.\n" 496 "https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=890215\n" 497 "https://bugs.kde.org/show_bug.cgi?id=421262\n"); 498 break; 499 } 500 printf ("Error, expected value >= 2^(%ld)\n", e); 501 printf ("got "); mpfr_dump (mp); 502 exit (1); 503 } 504 505 mpfr_set_ui_2exp (mp, 1, e, MPFR_RNDN); 506 ld = mpfr_get_ld (mp, MPFR_RNDD); 507 mpfr_set_ld (mp, ld, MPFR_RNDD); 508 /* mp is 2^e rounded down, thus should be <= 2^e */ 509 if (mpfr_cmp_ui_2exp (mp, 1, e) > 0) 510 { 511 printf ("Error, expected value <= 2^(%ld)\n", e); 512 printf ("got "); mpfr_dump (mp); 513 exit (1); 514 } 515 } 516 517 mpfr_clear (mp); 518#endif 519} 520 521int 522main (int argc, char *argv[]) 523{ 524 volatile long double d, e, maxp2; 525 mpfr_t x; 526 int i; 527 mpfr_exp_t emax; 528 529 tests_start_mpfr (); 530 mpfr_test_init (); 531 532 check_gcc33_bug (); 533 test_fixed_bugs (); 534 535 mpfr_init2 (x, MPFR_LDBL_MANT_DIG + 64); 536 537#if !defined(MPFR_ERRDIVZERO) 538 /* check NaN */ 539 mpfr_set_nan (x); 540 d = mpfr_get_ld (x, MPFR_RNDN); 541 check_set_get (d); 542#endif 543 544 /* check +0.0 and -0.0 */ 545 d = 0.0; 546 check_set_get (d); 547 d = DBL_NEG_ZERO; 548 check_set_get (d); 549 550 /* check that the sign of -0.0 is set */ 551 mpfr_set_ld (x, DBL_NEG_ZERO, MPFR_RNDN); 552 if (MPFR_IS_POS (x)) 553 { 554#if defined(HAVE_SIGNEDZ) 555 printf ("Error: sign of -0.0 is not set correctly\n"); 556 exit (1); 557#else 558 /* Non IEEE doesn't support negative zero yet */ 559 printf ("Warning: sign of -0.0 is not set correctly\n"); 560#endif 561 } 562 563#if !defined(MPFR_ERRDIVZERO) 564 /* check +Inf */ 565 mpfr_set_inf (x, 1); 566 d = mpfr_get_ld (x, MPFR_RNDN); 567 check_set_get (d); 568 569 /* check -Inf */ 570 mpfr_set_inf (x, -1); 571 d = mpfr_get_ld (x, MPFR_RNDN); 572 check_set_get (d); 573#endif 574 575 /* check the largest power of two */ 576 maxp2 = 1.0; 577 while (maxp2 < LDBL_MAX / 2.0) 578 maxp2 *= 2.0; 579 check_set_get (maxp2); 580 check_set_get (-maxp2); 581 582 d = LDBL_MAX; 583 e = d / 2.0; 584 if (e != maxp2) /* false under NetBSD/x86 */ 585 { 586 /* d = LDBL_MAX does not have excess precision. */ 587 check_set_get (d); 588 check_set_get (-d); 589 } 590 591 /* check the smallest power of two */ 592 d = 1.0; 593 while ((e = d / 2.0) != (long double) 0.0 && e != d) 594 d = e; 595 check_set_get (d); 596 check_set_get (-d); 597 598 /* check that 2^i, 2^i+1, 2^i-1 and 2^i-2^(i-2)-1 are correctly converted */ 599 d = 1.0; 600 for (i = 1; i < MPFR_LDBL_MANT_DIG + 8; i++) 601 { 602 d = 2.0 * d; /* d = 2^i */ 603 check_set_get (d); 604 if (d + 1.0 != d) 605 check_set_get (d + 1.0); 606 else 607 { 608 mpfr_set_ui_2exp (x, 1, i, MPFR_RNDN); 609 mpfr_add_ui (x, x, 1, MPFR_RNDN); 610 e = mpfr_get_ld (x, MPFR_RNDN); 611 check_set_get (e); 612 } 613 if (d - 1.0 != d) 614 check_set_get (d - 1.0); 615 else 616 { 617 mpfr_set_ui_2exp (x, 1, i, MPFR_RNDN); 618 mpfr_sub_ui (x, x, 1, MPFR_RNDN); 619 e = mpfr_get_ld (x, MPFR_RNDN); 620 check_set_get (e); 621 } 622 if (i < 3) 623 continue; 624 /* The following test triggers a failure in r10844 for i = 56, 625 with gcc -mpc64 on x86 (64-bit ABI). */ 626 mpfr_set_ui_2exp (x, 3, i-2, MPFR_RNDN); 627 mpfr_sub_ui (x, x, 1, MPFR_RNDN); 628 e = mpfr_get_ld (x, MPFR_RNDN); 629 check_set_get (e); 630 } 631 632 for (i = 0; i < 10000; i++) 633 { 634 mpfr_urandomb (x, RANDS); 635 d = mpfr_get_ld (x, MPFR_RNDN); 636 check_set_get (d); 637 } 638 639 /* check with reduced emax to exercise overflow */ 640 emax = mpfr_get_emax (); 641 mpfr_set_prec (x, 2); 642 set_emax (1); 643 mpfr_set_ld (x, (long double) 2.0, MPFR_RNDN); 644 MPFR_ASSERTN(mpfr_inf_p (x) && mpfr_sgn (x) > 0); 645 for (d = (long double) 2.0, i = 0; i < 13; i++, d *= d); 646 /* now d = 2^8192, or an infinity (e.g. with double or double-double) */ 647 mpfr_set_ld (x, d, MPFR_RNDN); 648 MPFR_ASSERTN(mpfr_inf_p (x) && mpfr_sgn (x) > 0); 649 set_emax (emax); 650 651 mpfr_clear (x); 652 653 test_small (); 654 655 check_subnormal (); 656#if !defined(MPFR_ERRDIVZERO) 657 check_overflow (); 658#endif 659 660 test_20140212 (); 661 bug_20160907 (); 662 663 tests_end_mpfr (); 664 665 return 0; 666} 667