alpar@1
|
1 |
/* glplib01.c (bignum arithmetic) */
|
alpar@1
|
2 |
|
alpar@1
|
3 |
/***********************************************************************
|
alpar@1
|
4 |
* This code is part of GLPK (GNU Linear Programming Kit).
|
alpar@1
|
5 |
*
|
alpar@1
|
6 |
* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008,
|
alpar@1
|
7 |
* 2009, 2010 Andrew Makhorin, Department for Applied Informatics,
|
alpar@1
|
8 |
* Moscow Aviation Institute, Moscow, Russia. All rights reserved.
|
alpar@1
|
9 |
* E-mail: <mao@gnu.org>.
|
alpar@1
|
10 |
*
|
alpar@1
|
11 |
* GLPK is free software: you can redistribute it and/or modify it
|
alpar@1
|
12 |
* under the terms of the GNU General Public License as published by
|
alpar@1
|
13 |
* the Free Software Foundation, either version 3 of the License, or
|
alpar@1
|
14 |
* (at your option) any later version.
|
alpar@1
|
15 |
*
|
alpar@1
|
16 |
* GLPK is distributed in the hope that it will be useful, but WITHOUT
|
alpar@1
|
17 |
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
alpar@1
|
18 |
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
alpar@1
|
19 |
* License for more details.
|
alpar@1
|
20 |
*
|
alpar@1
|
21 |
* You should have received a copy of the GNU General Public License
|
alpar@1
|
22 |
* along with GLPK. If not, see <http://www.gnu.org/licenses/>.
|
alpar@1
|
23 |
***********************************************************************/
|
alpar@1
|
24 |
|
alpar@1
|
25 |
#include "glpenv.h"
|
alpar@1
|
26 |
#include "glplib.h"
|
alpar@1
|
27 |
|
alpar@1
|
28 |
/***********************************************************************
|
alpar@1
|
29 |
* Two routines below are intended to multiply and divide unsigned
|
alpar@1
|
30 |
* integer numbers of arbitrary precision.
|
alpar@1
|
31 |
*
|
alpar@1
|
32 |
* The routines assume that an unsigned integer number is represented in
|
alpar@1
|
33 |
* the positional numeral system with the base 2^16 = 65536, i.e. each
|
alpar@1
|
34 |
* "digit" of the number is in the range [0, 65535] and represented as
|
alpar@1
|
35 |
* a 16-bit value of the unsigned short type. In other words, a number x
|
alpar@1
|
36 |
* has the following representation:
|
alpar@1
|
37 |
*
|
alpar@1
|
38 |
* n-1
|
alpar@1
|
39 |
* x = sum d[j] * 65536^j,
|
alpar@1
|
40 |
* j=0
|
alpar@1
|
41 |
*
|
alpar@1
|
42 |
* where n is the number of places (positions), and d[j] is j-th "digit"
|
alpar@1
|
43 |
* of x, 0 <= d[j] <= 65535.
|
alpar@1
|
44 |
***********************************************************************/
|
alpar@1
|
45 |
|
alpar@1
|
46 |
/***********************************************************************
|
alpar@1
|
47 |
* NAME
|
alpar@1
|
48 |
*
|
alpar@1
|
49 |
* bigmul - multiply unsigned integer numbers of arbitrary precision
|
alpar@1
|
50 |
*
|
alpar@1
|
51 |
* SYNOPSIS
|
alpar@1
|
52 |
*
|
alpar@1
|
53 |
* #include "glplib.h"
|
alpar@1
|
54 |
* void bigmul(int n, int m, unsigned short x[], unsigned short y[]);
|
alpar@1
|
55 |
*
|
alpar@1
|
56 |
* DESCRIPTION
|
alpar@1
|
57 |
*
|
alpar@1
|
58 |
* The routine bigmul multiplies unsigned integer numbers of arbitrary
|
alpar@1
|
59 |
* precision.
|
alpar@1
|
60 |
*
|
alpar@1
|
61 |
* n is the number of digits of multiplicand, n >= 1;
|
alpar@1
|
62 |
*
|
alpar@1
|
63 |
* m is the number of digits of multiplier, m >= 1;
|
alpar@1
|
64 |
*
|
alpar@1
|
65 |
* x is an array containing digits of the multiplicand in elements
|
alpar@1
|
66 |
* x[m], x[m+1], ..., x[n+m-1]. Contents of x[0], x[1], ..., x[m-1] are
|
alpar@1
|
67 |
* ignored on entry.
|
alpar@1
|
68 |
*
|
alpar@1
|
69 |
* y is an array containing digits of the multiplier in elements y[0],
|
alpar@1
|
70 |
* y[1], ..., y[m-1].
|
alpar@1
|
71 |
*
|
alpar@1
|
72 |
* On exit digits of the product are stored in elements x[0], x[1], ...,
|
alpar@1
|
73 |
* x[n+m-1]. The array y is not changed. */
|
alpar@1
|
74 |
|
alpar@1
|
75 |
void bigmul(int n, int m, unsigned short x[], unsigned short y[])
|
alpar@1
|
76 |
{ int i, j;
|
alpar@1
|
77 |
unsigned int t;
|
alpar@1
|
78 |
xassert(n >= 1);
|
alpar@1
|
79 |
xassert(m >= 1);
|
alpar@1
|
80 |
for (j = 0; j < m; j++) x[j] = 0;
|
alpar@1
|
81 |
for (i = 0; i < n; i++)
|
alpar@1
|
82 |
{ if (x[i+m])
|
alpar@1
|
83 |
{ t = 0;
|
alpar@1
|
84 |
for (j = 0; j < m; j++)
|
alpar@1
|
85 |
{ t += (unsigned int)x[i+m] * (unsigned int)y[j] +
|
alpar@1
|
86 |
(unsigned int)x[i+j];
|
alpar@1
|
87 |
x[i+j] = (unsigned short)t;
|
alpar@1
|
88 |
t >>= 16;
|
alpar@1
|
89 |
}
|
alpar@1
|
90 |
x[i+m] = (unsigned short)t;
|
alpar@1
|
91 |
}
|
alpar@1
|
92 |
}
|
alpar@1
|
93 |
return;
|
alpar@1
|
94 |
}
|
alpar@1
|
95 |
|
alpar@1
|
96 |
/***********************************************************************
|
alpar@1
|
97 |
* NAME
|
alpar@1
|
98 |
*
|
alpar@1
|
99 |
* bigdiv - divide unsigned integer numbers of arbitrary precision
|
alpar@1
|
100 |
*
|
alpar@1
|
101 |
* SYNOPSIS
|
alpar@1
|
102 |
*
|
alpar@1
|
103 |
* #include "glplib.h"
|
alpar@1
|
104 |
* void bigdiv(int n, int m, unsigned short x[], unsigned short y[]);
|
alpar@1
|
105 |
*
|
alpar@1
|
106 |
* DESCRIPTION
|
alpar@1
|
107 |
*
|
alpar@1
|
108 |
* The routine bigdiv divides one unsigned integer number of arbitrary
|
alpar@1
|
109 |
* precision by another with the algorithm described in [1].
|
alpar@1
|
110 |
*
|
alpar@1
|
111 |
* n is the difference between the number of digits of dividend and the
|
alpar@1
|
112 |
* number of digits of divisor, n >= 0.
|
alpar@1
|
113 |
*
|
alpar@1
|
114 |
* m is the number of digits of divisor, m >= 1.
|
alpar@1
|
115 |
*
|
alpar@1
|
116 |
* x is an array containing digits of the dividend in elements x[0],
|
alpar@1
|
117 |
* x[1], ..., x[n+m-1].
|
alpar@1
|
118 |
*
|
alpar@1
|
119 |
* y is an array containing digits of the divisor in elements y[0],
|
alpar@1
|
120 |
* y[1], ..., y[m-1]. The highest digit y[m-1] must be non-zero.
|
alpar@1
|
121 |
*
|
alpar@1
|
122 |
* On exit n+1 digits of the quotient are stored in elements x[m],
|
alpar@1
|
123 |
* x[m+1], ..., x[n+m], and m digits of the remainder are stored in
|
alpar@1
|
124 |
* elements x[0], x[1], ..., x[m-1]. The array y is changed but then
|
alpar@1
|
125 |
* restored.
|
alpar@1
|
126 |
*
|
alpar@1
|
127 |
* REFERENCES
|
alpar@1
|
128 |
*
|
alpar@1
|
129 |
* 1. D. Knuth. The Art of Computer Programming. Vol. 2: Seminumerical
|
alpar@1
|
130 |
* Algorithms. Stanford University, 1969. */
|
alpar@1
|
131 |
|
alpar@1
|
132 |
void bigdiv(int n, int m, unsigned short x[], unsigned short y[])
|
alpar@1
|
133 |
{ int i, j;
|
alpar@1
|
134 |
unsigned int t;
|
alpar@1
|
135 |
unsigned short d, q, r;
|
alpar@1
|
136 |
xassert(n >= 0);
|
alpar@1
|
137 |
xassert(m >= 1);
|
alpar@1
|
138 |
xassert(y[m-1] != 0);
|
alpar@1
|
139 |
/* special case when divisor has the only digit */
|
alpar@1
|
140 |
if (m == 1)
|
alpar@1
|
141 |
{ d = 0;
|
alpar@1
|
142 |
for (i = n; i >= 0; i--)
|
alpar@1
|
143 |
{ t = ((unsigned int)d << 16) + (unsigned int)x[i];
|
alpar@1
|
144 |
x[i+1] = (unsigned short)(t / y[0]);
|
alpar@1
|
145 |
d = (unsigned short)(t % y[0]);
|
alpar@1
|
146 |
}
|
alpar@1
|
147 |
x[0] = d;
|
alpar@1
|
148 |
goto done;
|
alpar@1
|
149 |
}
|
alpar@1
|
150 |
/* multiply dividend and divisor by a normalizing coefficient in
|
alpar@1
|
151 |
order to provide the condition y[m-1] >= base / 2 */
|
alpar@1
|
152 |
d = (unsigned short)(0x10000 / ((unsigned int)y[m-1] + 1));
|
alpar@1
|
153 |
if (d == 1)
|
alpar@1
|
154 |
x[n+m] = 0;
|
alpar@1
|
155 |
else
|
alpar@1
|
156 |
{ t = 0;
|
alpar@1
|
157 |
for (i = 0; i < n+m; i++)
|
alpar@1
|
158 |
{ t += (unsigned int)x[i] * (unsigned int)d;
|
alpar@1
|
159 |
x[i] = (unsigned short)t;
|
alpar@1
|
160 |
t >>= 16;
|
alpar@1
|
161 |
}
|
alpar@1
|
162 |
x[n+m] = (unsigned short)t;
|
alpar@1
|
163 |
t = 0;
|
alpar@1
|
164 |
for (j = 0; j < m; j++)
|
alpar@1
|
165 |
{ t += (unsigned int)y[j] * (unsigned int)d;
|
alpar@1
|
166 |
y[j] = (unsigned short)t;
|
alpar@1
|
167 |
t >>= 16;
|
alpar@1
|
168 |
}
|
alpar@1
|
169 |
}
|
alpar@1
|
170 |
/* main loop */
|
alpar@1
|
171 |
for (i = n; i >= 0; i--)
|
alpar@1
|
172 |
{ /* estimate and correct the current digit of quotient */
|
alpar@1
|
173 |
if (x[i+m] < y[m-1])
|
alpar@1
|
174 |
{ t = ((unsigned int)x[i+m] << 16) + (unsigned int)x[i+m-1];
|
alpar@1
|
175 |
q = (unsigned short)(t / (unsigned int)y[m-1]);
|
alpar@1
|
176 |
r = (unsigned short)(t % (unsigned int)y[m-1]);
|
alpar@1
|
177 |
if (q == 0) goto putq; else goto test;
|
alpar@1
|
178 |
}
|
alpar@1
|
179 |
q = 0;
|
alpar@1
|
180 |
r = x[i+m-1];
|
alpar@1
|
181 |
decr: q--; /* if q = 0 then q-- = 0xFFFF */
|
alpar@1
|
182 |
t = (unsigned int)r + (unsigned int)y[m-1];
|
alpar@1
|
183 |
r = (unsigned short)t;
|
alpar@1
|
184 |
if (t > 0xFFFF) goto msub;
|
alpar@1
|
185 |
test: t = (unsigned int)y[m-2] * (unsigned int)q;
|
alpar@1
|
186 |
if ((unsigned short)(t >> 16) > r) goto decr;
|
alpar@1
|
187 |
if ((unsigned short)(t >> 16) < r) goto msub;
|
alpar@1
|
188 |
if ((unsigned short)t > x[i+m-2]) goto decr;
|
alpar@1
|
189 |
msub: /* now subtract divisor multiplied by the current digit of
|
alpar@1
|
190 |
quotient from the current dividend */
|
alpar@1
|
191 |
if (q == 0) goto putq;
|
alpar@1
|
192 |
t = 0;
|
alpar@1
|
193 |
for (j = 0; j < m; j++)
|
alpar@1
|
194 |
{ t += (unsigned int)y[j] * (unsigned int)q;
|
alpar@1
|
195 |
if (x[i+j] < (unsigned short)t) t += 0x10000;
|
alpar@1
|
196 |
x[i+j] -= (unsigned short)t;
|
alpar@1
|
197 |
t >>= 16;
|
alpar@1
|
198 |
}
|
alpar@1
|
199 |
if (x[i+m] >= (unsigned short)t) goto putq;
|
alpar@1
|
200 |
/* perform correcting addition, because the current digit of
|
alpar@1
|
201 |
quotient is greater by one than its correct value */
|
alpar@1
|
202 |
q--;
|
alpar@1
|
203 |
t = 0;
|
alpar@1
|
204 |
for (j = 0; j < m; j++)
|
alpar@1
|
205 |
{ t += (unsigned int)x[i+j] + (unsigned int)y[j];
|
alpar@1
|
206 |
x[i+j] = (unsigned short)t;
|
alpar@1
|
207 |
t >>= 16;
|
alpar@1
|
208 |
}
|
alpar@1
|
209 |
putq: /* store the current digit of quotient */
|
alpar@1
|
210 |
x[i+m] = q;
|
alpar@1
|
211 |
}
|
alpar@1
|
212 |
/* divide divisor and remainder by the normalizing coefficient in
|
alpar@1
|
213 |
order to restore their original values */
|
alpar@1
|
214 |
if (d > 1)
|
alpar@1
|
215 |
{ t = 0;
|
alpar@1
|
216 |
for (i = m-1; i >= 0; i--)
|
alpar@1
|
217 |
{ t = (t << 16) + (unsigned int)x[i];
|
alpar@1
|
218 |
x[i] = (unsigned short)(t / (unsigned int)d);
|
alpar@1
|
219 |
t %= (unsigned int)d;
|
alpar@1
|
220 |
}
|
alpar@1
|
221 |
t = 0;
|
alpar@1
|
222 |
for (j = m-1; j >= 0; j--)
|
alpar@1
|
223 |
{ t = (t << 16) + (unsigned int)y[j];
|
alpar@1
|
224 |
y[j] = (unsigned short)(t / (unsigned int)d);
|
alpar@1
|
225 |
t %= (unsigned int)d;
|
alpar@1
|
226 |
}
|
alpar@1
|
227 |
}
|
alpar@1
|
228 |
done: return;
|
alpar@1
|
229 |
}
|
alpar@1
|
230 |
|
alpar@1
|
231 |
/**********************************************************************/
|
alpar@1
|
232 |
|
alpar@1
|
233 |
#if 0
|
alpar@1
|
234 |
#include <assert.h>
|
alpar@1
|
235 |
#include <stdio.h>
|
alpar@1
|
236 |
#include <stdlib.h>
|
alpar@1
|
237 |
#include "glprng.h"
|
alpar@1
|
238 |
|
alpar@1
|
239 |
#define N_MAX 7
|
alpar@1
|
240 |
/* maximal number of digits in multiplicand */
|
alpar@1
|
241 |
|
alpar@1
|
242 |
#define M_MAX 5
|
alpar@1
|
243 |
/* maximal number of digits in multiplier */
|
alpar@1
|
244 |
|
alpar@1
|
245 |
#define N_TEST 1000000
|
alpar@1
|
246 |
/* number of tests */
|
alpar@1
|
247 |
|
alpar@1
|
248 |
int main(void)
|
alpar@1
|
249 |
{ RNG *rand;
|
alpar@1
|
250 |
int d, j, n, m, test;
|
alpar@1
|
251 |
unsigned short x[N_MAX], y[M_MAX], z[N_MAX+M_MAX];
|
alpar@1
|
252 |
rand = rng_create_rand();
|
alpar@1
|
253 |
for (test = 1; test <= N_TEST; test++)
|
alpar@1
|
254 |
{ /* x[0,...,n-1] := multiplicand */
|
alpar@1
|
255 |
n = 1 + rng_unif_rand(rand, N_MAX-1);
|
alpar@1
|
256 |
assert(1 <= n && n <= N_MAX);
|
alpar@1
|
257 |
for (j = 0; j < n; j++)
|
alpar@1
|
258 |
{ d = rng_unif_rand(rand, 65536);
|
alpar@1
|
259 |
assert(0 <= d && d <= 65535);
|
alpar@1
|
260 |
x[j] = (unsigned short)d;
|
alpar@1
|
261 |
}
|
alpar@1
|
262 |
/* y[0,...,m-1] := multiplier */
|
alpar@1
|
263 |
m = 1 + rng_unif_rand(rand, M_MAX-1);
|
alpar@1
|
264 |
assert(1 <= m && m <= M_MAX);
|
alpar@1
|
265 |
for (j = 0; j < m; j++)
|
alpar@1
|
266 |
{ d = rng_unif_rand(rand, 65536);
|
alpar@1
|
267 |
assert(0 <= d && d <= 65535);
|
alpar@1
|
268 |
y[j] = (unsigned short)d;
|
alpar@1
|
269 |
}
|
alpar@1
|
270 |
if (y[m-1] == 0) y[m-1] = 1;
|
alpar@1
|
271 |
/* z[0,...,n+m-1] := x * y */
|
alpar@1
|
272 |
for (j = 0; j < n; j++) z[m+j] = x[j];
|
alpar@1
|
273 |
bigmul(n, m, z, y);
|
alpar@1
|
274 |
/* z[0,...,m-1] := z mod y, z[m,...,n+m-1] := z div y */
|
alpar@1
|
275 |
bigdiv(n, m, z, y);
|
alpar@1
|
276 |
/* z mod y must be 0 */
|
alpar@1
|
277 |
for (j = 0; j < m; j++) assert(z[j] == 0);
|
alpar@1
|
278 |
/* z div y must be x */
|
alpar@1
|
279 |
for (j = 0; j < n; j++) assert(z[m+j] == x[j]);
|
alpar@1
|
280 |
}
|
alpar@1
|
281 |
fprintf(stderr, "%d tests successfully passed\n", N_TEST);
|
alpar@1
|
282 |
rng_delete_rand(rand);
|
alpar@1
|
283 |
return 0;
|
alpar@1
|
284 |
}
|
alpar@1
|
285 |
#endif
|
alpar@1
|
286 |
|
alpar@1
|
287 |
/* eof */
|