1 /**************************************************************** 2 3 The author of this software is David M. Gay. 4 5 Copyright (C) 1998, 1999 by Lucent Technologies 6 All Rights Reserved 7 8 Permission to use, copy, modify, and distribute this software and 9 its documentation for any purpose and without fee is hereby 10 granted, provided that the above copyright notice appear in all 11 copies and that both that the copyright notice and this 12 permission notice and warranty disclaimer appear in supporting 13 documentation, and that the name of Lucent or any of its entities 14 not be used in advertising or publicity pertaining to 15 distribution of the software without specific, written prior 16 permission. 17 18 LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 19 INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. 20 IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY 21 SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 22 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 23 IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, 24 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 25 THIS SOFTWARE. 26 27 ****************************************************************/ 28 29 /* Please send bug reports to David M. Gay (dmg at acm dot org, 30 * with " at " changed at "@" and " dot " changed to "."). */ 31 32 #include "gdtoaimp.h" 33 34 static Bigint *freelist[Kmax+1]; 35 #ifndef Omit_Private_Memory 36 #ifndef PRIVATE_MEM 37 #define PRIVATE_MEM 2304 38 #endif 39 #define PRIVATE_mem ((PRIVATE_MEM+sizeof(double)-1)/sizeof(double)) 40 static double private_mem[PRIVATE_mem], *pmem_next = private_mem; 41 #endif 42 43 Bigint * 44 Balloc 45 #ifdef KR_headers 46 (k) int k; 47 #else 48 (int k) 49 #endif 50 { 51 int x; 52 Bigint *rv; 53 #ifndef Omit_Private_Memory 54 unsigned int len; 55 #endif 56 57 ACQUIRE_DTOA_LOCK(0); 58 if (k <= Kmax && (rv = freelist[k]) !=0) { 59 freelist[k] = rv->next; 60 } 61 else { 62 x = 1 << k; 63 #ifdef Omit_Private_Memory 64 rv = (Bigint *)MALLOC(sizeof(Bigint) + (x-1)*sizeof(ULong)); 65 #else 66 len = (sizeof(Bigint) + (x-1)*sizeof(ULong) + sizeof(double) - 1) 67 /sizeof(double); 68 if (k <= Kmax && pmem_next - private_mem + len <= PRIVATE_mem) { 69 rv = (Bigint*)pmem_next; 70 pmem_next += len; 71 } 72 else 73 rv = (Bigint*)MALLOC(len*sizeof(double)); 74 #endif 75 rv->k = k; 76 rv->maxwds = x; 77 } 78 FREE_DTOA_LOCK(0); 79 rv->sign = rv->wds = 0; 80 return rv; 81 } 82 83 void 84 Bfree 85 #ifdef KR_headers 86 (v) Bigint *v; 87 #else 88 (Bigint *v) 89 #endif 90 { 91 if (v) { 92 if (v->k > Kmax) { 93 free(v); 94 return; 95 } 96 ACQUIRE_DTOA_LOCK(0); 97 v->next = freelist[v->k]; 98 freelist[v->k] = v; 99 FREE_DTOA_LOCK(0); 100 } 101 } 102 103 int 104 lo0bits 105 #ifdef KR_headers 106 (y) ULong *y; 107 #else 108 (ULong *y) 109 #endif 110 { 111 register int k; 112 register ULong x = *y; 113 114 if (x & 7) { 115 if (x & 1) 116 return 0; 117 if (x & 2) { 118 *y = x >> 1; 119 return 1; 120 } 121 *y = x >> 2; 122 return 2; 123 } 124 k = 0; 125 if (!(x & 0xffff)) { 126 k = 16; 127 x >>= 16; 128 } 129 if (!(x & 0xff)) { 130 k += 8; 131 x >>= 8; 132 } 133 if (!(x & 0xf)) { 134 k += 4; 135 x >>= 4; 136 } 137 if (!(x & 0x3)) { 138 k += 2; 139 x >>= 2; 140 } 141 if (!(x & 1)) { 142 k++; 143 x >>= 1; 144 if (!x) 145 return 32; 146 } 147 *y = x; 148 return k; 149 } 150 151 Bigint * 152 multadd 153 #ifdef KR_headers 154 (b, m, a) Bigint *b; int m, a; 155 #else 156 (Bigint *b, int m, int a) /* multiply by m and add a */ 157 #endif 158 { 159 int i, wds; 160 #ifdef ULLong 161 ULong *x; 162 ULLong carry, y; 163 #else 164 ULong carry, *x, y; 165 #ifdef Pack_32 166 ULong xi, z; 167 #endif 168 #endif 169 Bigint *b1; 170 171 wds = b->wds; 172 x = b->x; 173 i = 0; 174 carry = a; 175 do { 176 #ifdef ULLong 177 y = *x * (ULLong)m + carry; 178 carry = y >> 32; 179 *x++ = y & 0xffffffffUL; 180 #else 181 #ifdef Pack_32 182 xi = *x; 183 y = (xi & 0xffff) * m + carry; 184 z = (xi >> 16) * m + (y >> 16); 185 carry = z >> 16; 186 *x++ = (z << 16) + (y & 0xffff); 187 #else 188 y = *x * m + carry; 189 carry = y >> 16; 190 *x++ = y & 0xffff; 191 #endif 192 #endif 193 } 194 while(++i < wds); 195 if (carry) { 196 if (wds >= b->maxwds) { 197 b1 = Balloc(b->k+1); 198 Bcopy(b1, b); 199 Bfree(b); 200 b = b1; 201 } 202 b->x[wds++] = carry; 203 b->wds = wds; 204 } 205 return b; 206 } 207 208 int 209 hi0bits_D2A 210 #ifdef KR_headers 211 (x) register ULong x; 212 #else 213 (register ULong x) 214 #endif 215 { 216 register int k = 0; 217 218 if (!(x & 0xffff0000)) { 219 k = 16; 220 x <<= 16; 221 } 222 if (!(x & 0xff000000)) { 223 k += 8; 224 x <<= 8; 225 } 226 if (!(x & 0xf0000000)) { 227 k += 4; 228 x <<= 4; 229 } 230 if (!(x & 0xc0000000)) { 231 k += 2; 232 x <<= 2; 233 } 234 if (!(x & 0x80000000)) { 235 k++; 236 if (!(x & 0x40000000)) 237 return 32; 238 } 239 return k; 240 } 241 242 Bigint * 243 i2b 244 #ifdef KR_headers 245 (i) int i; 246 #else 247 (int i) 248 #endif 249 { 250 Bigint *b; 251 252 b = Balloc(1); 253 b->x[0] = i; 254 b->wds = 1; 255 return b; 256 } 257 258 Bigint * 259 mult 260 #ifdef KR_headers 261 (a, b) Bigint *a, *b; 262 #else 263 (Bigint *a, Bigint *b) 264 #endif 265 { 266 Bigint *c; 267 int k, wa, wb, wc; 268 ULong *x, *xa, *xae, *xb, *xbe, *xc, *xc0; 269 ULong y; 270 #ifdef ULLong 271 ULLong carry, z; 272 #else 273 ULong carry, z; 274 #ifdef Pack_32 275 ULong z2; 276 #endif 277 #endif 278 279 if (a->wds < b->wds) { 280 c = a; 281 a = b; 282 b = c; 283 } 284 k = a->k; 285 wa = a->wds; 286 wb = b->wds; 287 wc = wa + wb; 288 if (wc > a->maxwds) 289 k++; 290 c = Balloc(k); 291 for(x = c->x, xa = x + wc; x < xa; x++) 292 *x = 0; 293 xa = a->x; 294 xae = xa + wa; 295 xb = b->x; 296 xbe = xb + wb; 297 xc0 = c->x; 298 #ifdef ULLong 299 for(; xb < xbe; xc0++) { 300 if ( (y = *xb++) !=0) { 301 x = xa; 302 xc = xc0; 303 carry = 0; 304 do { 305 z = *x++ * (ULLong)y + *xc + carry; 306 carry = z >> 32; 307 *xc++ = z & 0xffffffffUL; 308 } 309 while(x < xae); 310 *xc = carry; 311 } 312 } 313 #else 314 #ifdef Pack_32 315 for(; xb < xbe; xb++, xc0++) { 316 if ( (y = *xb & 0xffff) !=0) { 317 x = xa; 318 xc = xc0; 319 carry = 0; 320 do { 321 z = (*x & 0xffff) * y + (*xc & 0xffff) + carry; 322 carry = z >> 16; 323 z2 = (*x++ >> 16) * y + (*xc >> 16) + carry; 324 carry = z2 >> 16; 325 Storeinc(xc, z2, z); 326 } 327 while(x < xae); 328 *xc = carry; 329 } 330 if ( (y = *xb >> 16) !=0) { 331 x = xa; 332 xc = xc0; 333 carry = 0; 334 z2 = *xc; 335 do { 336 z = (*x & 0xffff) * y + (*xc >> 16) + carry; 337 carry = z >> 16; 338 Storeinc(xc, z, z2); 339 z2 = (*x++ >> 16) * y + (*xc & 0xffff) + carry; 340 carry = z2 >> 16; 341 } 342 while(x < xae); 343 *xc = z2; 344 } 345 } 346 #else 347 for(; xb < xbe; xc0++) { 348 if ( (y = *xb++) !=0) { 349 x = xa; 350 xc = xc0; 351 carry = 0; 352 do { 353 z = *x++ * y + *xc + carry; 354 carry = z >> 16; 355 *xc++ = z & 0xffff; 356 } 357 while(x < xae); 358 *xc = carry; 359 } 360 } 361 #endif 362 #endif 363 for(xc0 = c->x, xc = xc0 + wc; wc > 0 && !*--xc; --wc) ; 364 c->wds = wc; 365 return c; 366 } 367 368 static Bigint *p5s; 369 370 Bigint * 371 pow5mult 372 #ifdef KR_headers 373 (b, k) Bigint *b; int k; 374 #else 375 (Bigint *b, int k) 376 #endif 377 { 378 Bigint *b1, *p5, *p51; 379 int i; 380 static int p05[3] = { 5, 25, 125 }; 381 382 if ( (i = k & 3) !=0) 383 b = multadd(b, p05[i-1], 0); 384 385 if (!(k >>= 2)) 386 return b; 387 if ((p5 = p5s) == 0) { 388 /* first time */ 389 #ifdef MULTIPLE_THREADS 390 ACQUIRE_DTOA_LOCK(1); 391 if (!(p5 = p5s)) { 392 p5 = p5s = i2b(625); 393 p5->next = 0; 394 } 395 FREE_DTOA_LOCK(1); 396 #else 397 p5 = p5s = i2b(625); 398 p5->next = 0; 399 #endif 400 } 401 for(;;) { 402 if (k & 1) { 403 b1 = mult(b, p5); 404 Bfree(b); 405 b = b1; 406 } 407 if (!(k >>= 1)) 408 break; 409 if ((p51 = p5->next) == 0) { 410 #ifdef MULTIPLE_THREADS 411 ACQUIRE_DTOA_LOCK(1); 412 if (!(p51 = p5->next)) { 413 p51 = p5->next = mult(p5,p5); 414 p51->next = 0; 415 } 416 FREE_DTOA_LOCK(1); 417 #else 418 p51 = p5->next = mult(p5,p5); 419 p51->next = 0; 420 #endif 421 } 422 p5 = p51; 423 } 424 return b; 425 } 426 427 Bigint * 428 lshift 429 #ifdef KR_headers 430 (b, k) Bigint *b; int k; 431 #else 432 (Bigint *b, int k) 433 #endif 434 { 435 int i, k1, n, n1; 436 Bigint *b1; 437 ULong *x, *x1, *xe, z; 438 439 n = k >> kshift; 440 k1 = b->k; 441 n1 = n + b->wds + 1; 442 for(i = b->maxwds; n1 > i; i <<= 1) 443 k1++; 444 b1 = Balloc(k1); 445 x1 = b1->x; 446 for(i = 0; i < n; i++) 447 *x1++ = 0; 448 x = b->x; 449 xe = x + b->wds; 450 if (k &= kmask) { 451 #ifdef Pack_32 452 k1 = 32 - k; 453 z = 0; 454 do { 455 *x1++ = *x << k | z; 456 z = *x++ >> k1; 457 } 458 while(x < xe); 459 if ((*x1 = z) !=0) 460 ++n1; 461 #else 462 k1 = 16 - k; 463 z = 0; 464 do { 465 *x1++ = *x << k & 0xffff | z; 466 z = *x++ >> k1; 467 } 468 while(x < xe); 469 if (*x1 = z) 470 ++n1; 471 #endif 472 } 473 else do 474 *x1++ = *x++; 475 while(x < xe); 476 b1->wds = n1 - 1; 477 Bfree(b); 478 return b1; 479 } 480 481 int 482 cmp 483 #ifdef KR_headers 484 (a, b) Bigint *a, *b; 485 #else 486 (Bigint *a, Bigint *b) 487 #endif 488 { 489 ULong *xa, *xa0, *xb, *xb0; 490 int i, j; 491 492 i = a->wds; 493 j = b->wds; 494 #ifdef DEBUG 495 if (i > 1 && !a->x[i-1]) 496 Bug("cmp called with a->x[a->wds-1] == 0"); 497 if (j > 1 && !b->x[j-1]) 498 Bug("cmp called with b->x[b->wds-1] == 0"); 499 #endif 500 if (i -= j) 501 return i; 502 xa0 = a->x; 503 xa = xa0 + j; 504 xb0 = b->x; 505 xb = xb0 + j; 506 for(;;) { 507 if (*--xa != *--xb) 508 return *xa < *xb ? -1 : 1; 509 if (xa <= xa0) 510 break; 511 } 512 return 0; 513 } 514 515 Bigint * 516 diff 517 #ifdef KR_headers 518 (a, b) Bigint *a, *b; 519 #else 520 (Bigint *a, Bigint *b) 521 #endif 522 { 523 Bigint *c; 524 int i, wa, wb; 525 ULong *xa, *xae, *xb, *xbe, *xc; 526 #ifdef ULLong 527 ULLong borrow, y; 528 #else 529 ULong borrow, y; 530 #ifdef Pack_32 531 ULong z; 532 #endif 533 #endif 534 535 i = cmp(a,b); 536 if (!i) { 537 c = Balloc(0); 538 c->wds = 1; 539 c->x[0] = 0; 540 return c; 541 } 542 if (i < 0) { 543 c = a; 544 a = b; 545 b = c; 546 i = 1; 547 } 548 else 549 i = 0; 550 c = Balloc(a->k); 551 c->sign = i; 552 wa = a->wds; 553 xa = a->x; 554 xae = xa + wa; 555 wb = b->wds; 556 xb = b->x; 557 xbe = xb + wb; 558 xc = c->x; 559 borrow = 0; 560 #ifdef ULLong 561 do { 562 y = (ULLong)*xa++ - *xb++ - borrow; 563 borrow = y >> 32 & 1UL; 564 *xc++ = y & 0xffffffffUL; 565 } 566 while(xb < xbe); 567 while(xa < xae) { 568 y = *xa++ - borrow; 569 borrow = y >> 32 & 1UL; 570 *xc++ = y & 0xffffffffUL; 571 } 572 #else 573 #ifdef Pack_32 574 do { 575 y = (*xa & 0xffff) - (*xb & 0xffff) - borrow; 576 borrow = (y & 0x10000) >> 16; 577 z = (*xa++ >> 16) - (*xb++ >> 16) - borrow; 578 borrow = (z & 0x10000) >> 16; 579 Storeinc(xc, z, y); 580 } 581 while(xb < xbe); 582 while(xa < xae) { 583 y = (*xa & 0xffff) - borrow; 584 borrow = (y & 0x10000) >> 16; 585 z = (*xa++ >> 16) - borrow; 586 borrow = (z & 0x10000) >> 16; 587 Storeinc(xc, z, y); 588 } 589 #else 590 do { 591 y = *xa++ - *xb++ - borrow; 592 borrow = (y & 0x10000) >> 16; 593 *xc++ = y & 0xffff; 594 } 595 while(xb < xbe); 596 while(xa < xae) { 597 y = *xa++ - borrow; 598 borrow = (y & 0x10000) >> 16; 599 *xc++ = y & 0xffff; 600 } 601 #endif 602 #endif 603 while(!*--xc) 604 wa--; 605 c->wds = wa; 606 return c; 607 } 608 609 double 610 b2d 611 #ifdef KR_headers 612 (a, e) Bigint *a; int *e; 613 #else 614 (Bigint *a, int *e) 615 #endif 616 { 617 ULong *xa, *xa0, w, y, z; 618 int k; 619 double d; 620 #ifdef VAX 621 ULong d0, d1; 622 #else 623 #define d0 word0(d) 624 #define d1 word1(d) 625 #endif 626 627 xa0 = a->x; 628 xa = xa0 + a->wds; 629 y = *--xa; 630 #ifdef DEBUG 631 if (!y) Bug("zero y in b2d"); 632 #endif 633 k = hi0bits(y); 634 *e = 32 - k; 635 #ifdef Pack_32 636 if (k < Ebits) { 637 d0 = Exp_1 | y >> Ebits - k; 638 w = xa > xa0 ? *--xa : 0; 639 d1 = y << (32-Ebits) + k | w >> Ebits - k; 640 goto ret_d; 641 } 642 z = xa > xa0 ? *--xa : 0; 643 if (k -= Ebits) { 644 d0 = Exp_1 | y << k | z >> 32 - k; 645 y = xa > xa0 ? *--xa : 0; 646 d1 = z << k | y >> 32 - k; 647 } 648 else { 649 d0 = Exp_1 | y; 650 d1 = z; 651 } 652 #else 653 if (k < Ebits + 16) { 654 z = xa > xa0 ? *--xa : 0; 655 d0 = Exp_1 | y << k - Ebits | z >> Ebits + 16 - k; 656 w = xa > xa0 ? *--xa : 0; 657 y = xa > xa0 ? *--xa : 0; 658 d1 = z << k + 16 - Ebits | w << k - Ebits | y >> 16 + Ebits - k; 659 goto ret_d; 660 } 661 z = xa > xa0 ? *--xa : 0; 662 w = xa > xa0 ? *--xa : 0; 663 k -= Ebits + 16; 664 d0 = Exp_1 | y << k + 16 | z << k | w >> 16 - k; 665 y = xa > xa0 ? *--xa : 0; 666 d1 = w << k + 16 | y << k; 667 #endif 668 ret_d: 669 #ifdef VAX 670 word0(d) = d0 >> 16 | d0 << 16; 671 word1(d) = d1 >> 16 | d1 << 16; 672 #endif 673 return dval(d); 674 } 675 #undef d0 676 #undef d1 677 678 Bigint * 679 d2b 680 #ifdef KR_headers 681 (d, e, bits) double d; int *e, *bits; 682 #else 683 (double d, int *e, int *bits) 684 #endif 685 { 686 Bigint *b; 687 #ifndef Sudden_Underflow 688 int i; 689 #endif 690 int de, k; 691 ULong *x, y, z; 692 #ifdef VAX 693 ULong d0, d1; 694 d0 = word0(d) >> 16 | word0(d) << 16; 695 d1 = word1(d) >> 16 | word1(d) << 16; 696 #else 697 #define d0 word0(d) 698 #define d1 word1(d) 699 #endif 700 701 #ifdef Pack_32 702 b = Balloc(1); 703 #else 704 b = Balloc(2); 705 #endif 706 x = b->x; 707 708 z = d0 & Frac_mask; 709 d0 &= 0x7fffffff; /* clear sign bit, which we ignore */ 710 #ifdef Sudden_Underflow 711 de = (int)(d0 >> Exp_shift); 712 #ifndef IBM 713 z |= Exp_msk11; 714 #endif 715 #else 716 if ( (de = (int)(d0 >> Exp_shift)) !=0) 717 z |= Exp_msk1; 718 #endif 719 #ifdef Pack_32 720 if ( (y = d1) !=0) { 721 if ( (k = lo0bits(&y)) !=0) { 722 x[0] = y | z << 32 - k; 723 z >>= k; 724 } 725 else 726 x[0] = y; 727 #ifndef Sudden_Underflow 728 i = 729 #endif 730 b->wds = (x[1] = z) !=0 ? 2 : 1; 731 } 732 else { 733 #ifdef DEBUG 734 if (!z) 735 Bug("Zero passed to d2b"); 736 #endif 737 k = lo0bits(&z); 738 x[0] = z; 739 #ifndef Sudden_Underflow 740 i = 741 #endif 742 b->wds = 1; 743 k += 32; 744 } 745 #else 746 if ( (y = d1) !=0) { 747 if ( (k = lo0bits(&y)) !=0) 748 if (k >= 16) { 749 x[0] = y | z << 32 - k & 0xffff; 750 x[1] = z >> k - 16 & 0xffff; 751 x[2] = z >> k; 752 i = 2; 753 } 754 else { 755 x[0] = y & 0xffff; 756 x[1] = y >> 16 | z << 16 - k & 0xffff; 757 x[2] = z >> k & 0xffff; 758 x[3] = z >> k+16; 759 i = 3; 760 } 761 else { 762 x[0] = y & 0xffff; 763 x[1] = y >> 16; 764 x[2] = z & 0xffff; 765 x[3] = z >> 16; 766 i = 3; 767 } 768 } 769 else { 770 #ifdef DEBUG 771 if (!z) 772 Bug("Zero passed to d2b"); 773 #endif 774 k = lo0bits(&z); 775 if (k >= 16) { 776 x[0] = z; 777 i = 0; 778 } 779 else { 780 x[0] = z & 0xffff; 781 x[1] = z >> 16; 782 i = 1; 783 } 784 k += 32; 785 } 786 while(!x[i]) 787 --i; 788 b->wds = i + 1; 789 #endif 790 #ifndef Sudden_Underflow 791 if (de) { 792 #endif 793 #ifdef IBM 794 *e = (de - Bias - (P-1) << 2) + k; 795 *bits = 4*P + 8 - k - hi0bits(word0(d) & Frac_mask); 796 #else 797 *e = de - Bias - (P-1) + k; 798 *bits = P - k; 799 #endif 800 #ifndef Sudden_Underflow 801 } 802 else { 803 *e = de - Bias - (P-1) + 1 + k; 804 #ifdef Pack_32 805 *bits = 32*i - hi0bits(x[i-1]); 806 #else 807 *bits = (i+2)*16 - hi0bits(x[i]); 808 #endif 809 } 810 #endif 811 return b; 812 } 813 #undef d0 814 #undef d1 815 816 CONST double 817 #ifdef IEEE_Arith 818 bigtens[] = { 1e16, 1e32, 1e64, 1e128, 1e256 }; 819 CONST double tinytens[] = { 1e-16, 1e-32, 1e-64, 1e-128, 1e-256 820 }; 821 #else 822 #ifdef IBM 823 bigtens[] = { 1e16, 1e32, 1e64 }; 824 CONST double tinytens[] = { 1e-16, 1e-32, 1e-64 }; 825 #else 826 bigtens[] = { 1e16, 1e32 }; 827 CONST double tinytens[] = { 1e-16, 1e-32 }; 828 #endif 829 #endif 830 831 CONST double 832 tens[] = { 833 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 834 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 835 1e20, 1e21, 1e22 836 #ifdef VAX 837 , 1e23, 1e24 838 #endif 839 }; 840 841 char * 842 #ifdef KR_headers 843 strcp_D2A(a, b) char *a; char *b; 844 #else 845 strcp_D2A(char *a, CONST char *b) 846 #endif 847 { 848 while(*a = *b++) 849 a++; 850 return a; 851 } 852 853 #ifdef NO_STRING_H 854 855 Char * 856 #ifdef KR_headers 857 memcpy_D2A(a, b, len) Char *a; Char *b; size_t len; 858 #else 859 memcpy_D2A(void *a1, void *b1, size_t len) 860 #endif 861 { 862 register char *a = (char*)a1, *ae = a + len; 863 register char *b = (char*)b1, *a0 = a; 864 while(a < ae) 865 *a++ = *b++; 866 return a0; 867 } 868 869 #endif /* NO_STRING_H */ 870