summaryrefslogtreecommitdiff
path: root/mpn/generic/div_qr_1.c
blob: 82f4b5c239fbd1f1750acbfa5153111ffb1486e7 (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
/* mpn_div_qr_1 -- mpn by limb division.

   Contributed to the GNU project by Niels Möller and Torbjörn Granlund

Copyright 1991, 1993, 1994, 1996, 1998-2000, 2002, 2003, 2013 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 Lesser General Public License as published by
the Free Software Foundation; either version 3 of the License, 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 Lesser General Public
License for more details.

You should have received a copy of the GNU Lesser General Public License
along with the GNU MP Library.  If not, see https://www.gnu.org/licenses/.  */

#include "gmp.h"
#include "gmp-impl.h"
#include "longlong.h"

/* FIXME: Add proper tuning */
#ifndef DIV_QR_1_NORM_THRESHOLD
#define DIV_QR_1_NORM_THRESHOLD 3
#endif
#ifndef DIV_QR_1_UNNORM_THRESHOLD
#define DIV_QR_1_UNNORM_THRESHOLD 3
#endif

#if GMP_NAIL_BITS > 0
#error Nail bits not supported
#endif

/* Divides {up, n} by d. Writes the n-1 low quotient limbs at {qp,
 * n-1}, and the high quote limb at *qh. Returns remainder. */
mp_limb_t
mpn_div_qr_1 (mp_ptr qp, mp_limb_t *qh, mp_srcptr up, mp_size_t n,
	      mp_limb_t d)
{
  unsigned cnt;
  mp_limb_t uh;

  ASSERT (n > 0);
  ASSERT (d > 0);

  if (d & GMP_NUMB_HIGHBIT)
    {
      /* Normalized case */
      mp_limb_t dinv, q;

      uh = up[--n];

      q = (uh >= d);
      *qh = q;
      uh -= (-q) & d;

      if (BELOW_THRESHOLD (n, DIV_QR_1_NORM_THRESHOLD))
	{
	  cnt = 0;
	plain:
	  while (n > 0)
	    {
	      mp_limb_t ul = up[--n];
	      udiv_qrnnd (qp[n], uh, uh, ul, d);
	    }
	  return uh >> cnt;
	}
      invert_limb (dinv, d);
      return mpn_div_qr_1n_pi1 (qp, up, n, uh, d, dinv);
    }
  else
    {
      /* Unnormalized case */
      mp_limb_t dinv, ul;

      if (! UDIV_NEEDS_NORMALIZATION
	  && BELOW_THRESHOLD (n, DIV_QR_1_UNNORM_THRESHOLD))
	{
	  uh = up[--n];
	  udiv_qrnnd (*qh, uh, CNST_LIMB(0), uh, d);
	  cnt = 0;
	  goto plain;
	}

      count_leading_zeros (cnt, d);
      d <<= cnt;

#if HAVE_NATIVE_div_qr_1u_pi1
      /* FIXME: Call loop doing on-the-fly normalization */
#endif

      /* Shift up front, use qp area for shifted copy. A bit messy,
	 since we have only n-1 limbs available, and shift the high
	 limb manually. */
      uh = up[--n];
      ul = (uh << cnt) | mpn_lshift (qp, up, n, cnt);
      uh >>= (GMP_LIMB_BITS - cnt);

      if (UDIV_NEEDS_NORMALIZATION
	  && BELOW_THRESHOLD (n, DIV_QR_1_UNNORM_THRESHOLD))
	{
	  udiv_qrnnd (*qh, uh, uh, ul, d);
	  up = qp;
	  goto plain;
	}
      invert_limb (dinv, d);

      udiv_qrnnd_preinv (*qh, uh, uh, ul, d, dinv);
      return mpn_div_qr_1n_pi1 (qp, qp, n, uh, d, dinv) >> cnt;
    }
}