summaryrefslogtreecommitdiff
path: root/ghc/runtime/gmp/mpz_div.c
blob: a27cde9ec9a2378f656103214d62aa09c9dd2931 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
/* mpz_div -- divide two integers and produce a 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"

void
#ifdef __STDC__
mpz_div (MP_INT *quot, const MP_INT *num, const MP_INT *den)
#else
mpz_div (quot, num, den)
     MP_INT *quot;
     const MP_INT *num;
     const MP_INT *den;
#endif
{
  mp_srcptr np, dp;
  mp_ptr qp, rp;
  mp_size nsize = num->size;
  mp_size dsize = den->size;
  mp_size qsize, rsize;
  mp_size sign_quotient = nsize ^ dsize;
  unsigned normalization_steps;

  nsize = ABS (nsize);
  dsize = ABS (dsize);

  /* Ensure space is enough for quotient. */

  qsize = nsize - dsize + 1;	/* qsize cannot be bigger than this.  */
  if (qsize <= 0)
    {
      quot->size = 0;
      return;
    }

  if (quot->alloc < qsize)
    _mpz_realloc (quot, qsize);

  qp = quot->d;
  np = num->d;
  dp = den->d;
  rp = (mp_ptr) alloca ((nsize + 1) * BYTES_PER_MP_LIMB);

  count_leading_zeros (normalization_steps, dp[dsize - 1]);

  /* Normalize the denominator and the numerator.  */
  if (normalization_steps != 0)
    {
      mp_ptr tp;
      mp_limb ndigit;

      /* Shift up the denominator setting the most significant bit of
	 the most significant word.  Use temporary storage not to clobber
	 the original contents of the denominator.  */
      tp = (mp_ptr) alloca (dsize * BYTES_PER_MP_LIMB);
      (void) mpn_lshift (tp, dp, dsize, normalization_steps);
      dp = tp;

      /* Shift up the numerator, possibly introducing a new most
	 significant word.  Move the shifted numerator in the remainder
	 meanwhile.  */
      ndigit = mpn_lshift (rp, np, nsize, normalization_steps);
      if (ndigit != 0)
	{
	  rp[nsize] = ndigit;
	  rsize = nsize + 1;
	}
      else
	rsize = nsize;
    }
  else
    {
      /* The denominator is already normalized, as required.
	 Copy it to temporary space if it overlaps with the quotient.  */
      if (dp == qp)
	{
	  dp = (mp_ptr) alloca (dsize * BYTES_PER_MP_LIMB);
	  MPN_COPY ((mp_ptr) dp, qp, dsize);
	}

      /* Move the numerator to the remainder.  */
      MPN_COPY (rp, np, nsize);
      rsize = nsize;
    }

  qsize = rsize - dsize + mpn_div (qp, rp, rsize, dp, dsize);

  /* Normalize the quotient.  We may have at most one leading
     zero-word, so no loop is needed.  */
  if (qsize > 0)
    qsize -= (qp[qsize - 1] == 0);

  if (sign_quotient < 0)
    qsize = -qsize;
  quot->size = qsize;

  alloca (0);
}