home *** CD-ROM | disk | FTP | other *** search
- /* mpn_div -- Divide natural numbers, producing both remainder and
- quotient.
-
- Copyright (C) 1991 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 the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- 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 a copy of the GNU General Public License
- along with the GNU MP Library; see the file COPYING. If not, write to
- the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
-
- #include "gmp.h"
- #include "gmp-impl.h"
- #include "longlong.h"
-
- /* Divide num (NUM_PTR/NUM_SIZE) by den (DEN_PTR/DEN_SIZE) and write
- the quotient at QUOT_PTR and the remainder at NUM_PTR.
-
- Return 0 or 1, depending on if the quotient size is (NSIZE - DSIZE)
- or (NSIZE - DSIZE + 1).
-
- Argument constraints:
- 1. The most significant bit of d must be set.
- 2. QUOT_PTR != DEN_PTR and QUOT_PTR != NUM_PTR, i.e. the quotient storage
- area must be distinct from either input operands.
-
- The exact sizes of the quotient and remainder must be determined
- by the caller, in spite of the return value. The return value just
- informs the caller about if the highest digit is written or not, and
- it may very well be 0. */
-
- /* THIS WILL BE IMPROVED SOON. MORE COMMENTS AND FASTER CODE. */
-
- mp_size
- #ifdef __STDC__
- mpn_div (mp_ptr quot_ptr,
- mp_ptr num_ptr, mp_size num_size,
- mp_srcptr den_ptr, mp_size den_size)
- #else
- mpn_div (quot_ptr, num_ptr, num_size, den_ptr, den_size)
- mp_ptr quot_ptr;
- mp_ptr num_ptr;
- mp_size num_size;
- mp_srcptr den_ptr;
- mp_size den_size;
- #endif
- {
- mp_size q_is_long = 0;
-
- switch (den_size)
- {
- case 0:
- /* We are asked to divide by zero, so go ahead and do it!
- (To make the compiler not remove this statement, assign NUM_SIZE
- and fall through.) */
- num_size = 1 / den_size;
-
- case 1:
- {
- mp_size i;
- mp_limb n1, n0;
- mp_limb d;
-
- d = den_ptr[0];
- i = num_size - 1;
- n1 = num_ptr[i];
- i--;
-
- if (n1 >= d)
- {
- q_is_long = 1;
- n1 = 0;
- i++;
- }
-
- for (; i >= 0; i--)
- {
- n0 = num_ptr[i];
- udiv_qrnnd (quot_ptr[i], n1, n1, n0, d);
- }
-
- num_ptr[0] = n1;
- }
- break;
-
- case 2:
- {
- mp_size i;
- mp_limb n0, n1, n2;
- mp_limb d0, d1;
-
- num_ptr += num_size - 2;
- d0 = den_ptr[1];
- d1 = den_ptr[0];
- n0 = num_ptr[1];
- n1 = num_ptr[0];
-
- if (n0 >= d0)
- {
- q_is_long = 1;
- n1 = n0;
- n0 = 0;
- num_ptr++;
- num_size++;
- }
-
- for (i = num_size - den_size - 1; i >= 0; i--)
- {
- mp_limb q;
- mp_limb r;
-
- num_ptr--;
- if (n0 == d0)
- {
- /* Q should be either 111..111 or 111..110. Need special
- treatment of this rare case as normal division would
- give overflow. */
- q = ~0;
-
- r = n1 + d0;
- if (r < d0) /* Carry in the addition? */
- {
- n2 = num_ptr[0];
-
- add_ssaaaa (n0, n1, r - d1, n2, 0, d1);
- quot_ptr[i] = q;
- continue;
- }
- n0 = d1 - (d1 != 0);
- n1 = -d1;
- }
- else
- {
- udiv_qrnnd (q, r, n0, n1, d0);
- umul_ppmm (n0, n1, d1, q);
- }
-
- n2 = num_ptr[0];
- q_test:
- if (n0 > r || (n0 == r && n1 > n2))
- {
- /* The estimated Q was too large. */
- q--;
-
- sub_ddmmss (n0, n1, n0, n1, 0, d1);
- r += d0;
- if (r >= d0) /* If not carry, test q again. */
- goto q_test;
- }
-
- quot_ptr[i] = q;
- sub_ddmmss (n0, n1, r, n2, n0, n1);
- }
- num_ptr[1] = n0;
- num_ptr[0] = n1;
- }
- break;
-
- default:
- {
- mp_size i;
- mp_limb d0 = den_ptr[den_size - 1];
- mp_limb d1 = den_ptr[den_size - 2];
- mp_limb n0 = num_ptr[num_size - 1];
- int ugly_hack_flag = 0;
-
- if (n0 >= d0)
- {
-
- /* There's a problem with this case, which shows up later in the
- code. q becomes 1 (and sometimes 0) the first time when
- we've been here, and w_cy == 0 after the main do-loops below.
- But c = num_ptr[j] reads rubbish outside the num_ptr vector!
- Maybe I can solve this cleanly when I fix the early-end
- optimization here in the default case. For now, I change the
- add_back entering condition, to kludge. Leaving the stray
- memref behind!
-
- HACK: Added ugly_hack_flag to make it work. */
-
- q_is_long = 1;
- n0 = 0;
- num_size++;
- ugly_hack_flag = 1;
- }
-
- num_ptr += num_size;
- den_ptr += den_size;
- for (i = num_size - den_size - 1; i >= 0; i--)
- {
- mp_limb q;
- mp_limb n1;
- mp_limb w_cy;
- mp_limb d, c;
- mp_size j;
-
- num_ptr--;
- if (n0 == d0)
- /* This might over-estimate q, but it's probably not worth
- the extra code here to find out. */
- q = ~0;
- else
- {
- mp_limb r;
-
- udiv_qrnnd (q, r, n0, num_ptr[-1], d0);
- umul_ppmm (n1, n0, d1, q);
-
- while (n1 > r || (n1 == r && n0 > num_ptr[-2]))
- {
- q--;
- r += d0;
- if (r < d0) /* I.e. "carry in previous addition?" */
- break;
- n1 -= n0 < d1;
- n0 -= d1;
- }
- }
-
- w_cy = 0;
- j = -den_size;
- do
- {
- d = den_ptr[j];
- c = num_ptr[j];
- umul_ppmm (n1, n0, d, q);
- n0 += w_cy;
- w_cy = (n0 < w_cy) + n1;
- n0 = c - n0;
- num_ptr[j] = n0;
- if (n0 > c)
- goto cy_loop;
- ncy_loop:
- j++;
- }
- while (j < 0);
-
- if (ugly_hack_flag)
- {
- c = 0;
- ugly_hack_flag = 0;
- }
- else
- c = num_ptr[j];
- if (c >= w_cy)
- goto store_q;
- goto add_back;
-
- do
- {
- d = den_ptr[j];
- c = num_ptr[j];
- umul_ppmm (n1, n0, d, q);
- n0 += w_cy;
- w_cy = (n0 < w_cy) + n1;
- n0 = c - n0 - 1;
- num_ptr[j] = n0;
- if (n0 < c)
- goto ncy_loop;
- cy_loop:
- j++;
- }
- while (j < 0);
-
- if (ugly_hack_flag)
- {
- c = 0;
- ugly_hack_flag = 0;
- }
- else
- c = num_ptr[j];
- w_cy++;
- if (c >= w_cy)
- goto store_q;
-
- add_back:
- j = -den_size;
- do
- {
- d = den_ptr[j];
- n0 = num_ptr[j] + d;
- num_ptr[j] = n0;
- if (n0 < d)
- goto ab_cy_loop;
- ab_ncy_loop:
- j++;
- }
- while (j < 0);
- abort (); /* We should always have a carry out! */
-
- do
- {
- d = den_ptr[j];
- n0 = num_ptr[j] + d + 1;
- num_ptr[j] = n0;
- if (n0 > d)
- goto ab_ncy_loop;
- ab_cy_loop:
- j++;
- }
- while (j < 0);
- q--;
-
- store_q:
- quot_ptr[i] = q;
- }
- }
- }
-
- return q_is_long;
- }
-