File: divide.c

package info (click to toggle)
nickle 2.68-1
  • links: PTS
  • area: main
  • in suites: lenny
  • size: 2,336 kB
  • ctags: 3,288
  • sloc: ansic: 31,198; yacc: 1,860; lex: 858; sh: 830; makefile: 229
file content (292 lines) | stat: -rw-r--r-- 7,056 bytes parent folder | download | duplicates (7)
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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
/* $Header$ */

/*
 * Copyright © 1988-2004 Keith Packard and Bart Massey.
 * All Rights Reserved.  See the file COPYING in this directory
 * for licensing information.
 */

/*
 * divide.c
 *
 * perform natural number division
 */

#include	"nickle.h"

#undef CHECK
#undef DEBUG

#if defined(DEBUG) || defined(CHECK)
void
pr (Natural *n)
{
	int	i;

	for (i = 0; i < NaturalLength(n); i++)
		(void) printf ("%u ", (unsigned int) NaturalDigits(n)[i]);
	(void) putchar ('\n');
}

void
prs (char *s, Natural *n)
{
	(void) printf ("%s ", s);
	pr (n);
}
#endif

/*
 * Return shift amount needed to normalize d (MSB (d << shift) == 1)
 */
static int normalize (digit d)
{
    int	    n;
    digit   msb;

    n = 0;
    msb = 1 << (LBASE2 - 1);
    while (!(d & msb))
    {
	n++;
	d <<= 1;
    }
    return n;
}

Natural *
NaturalDivide (Natural *a, Natural *b, Natural **remp)
{
    ENTER ();
    int		    quolen, remlen;
    Natural	    *quo, *rem;
    int		    index;
    double_digit    temp;
    digit	    temp2;
    Natural	    *partial;
    int		    offset;
    digit	    dividend1;	/* first digit of dividend */
    digit	    dividend2;	/* second digit of dividend */
    digit	    dividend3;	/* third digit of dividend */
    digit	    dividend4;	/* fourth digit of dividend */
    digit	    divisor1;	/* first digit of divisor */
    digit	    divisor2;	/* second digit of divisor */
    digit	    divisor3;	/* third digit of divisor */
    digit	    divisorc;	/* combination of first two digits */
    digit	    divisorc2;	/* combination of digits 2 and 3 */
    digit	    d;
    digit	    carry;
    int		    normal;
    
    if (NaturalLess (a, b)) {
	quo = zero_natural;
	rem = a;
    } else if (oneNp (b)) {
	quo = a;
	rem = zero_natural;
    } else if ((offset = NaturalPowerOfTwo (b)) >= 0) {
	quo = NaturalRsl (a, offset);
	rem = NaturalMask (a, offset);
    } else {
	quolen = a->length - b->length + 1;
	quo = AllocNatural (quolen);
	NaturalDigits(quo)[a->length - b->length] = 0;

	partial = AllocNatural (b->length + 1);

	remlen = a->length + 1;
	rem = AllocNatural (remlen);
	NaturalCopy (a, rem);
	NaturalDigits(rem)[remlen - 1] = 0;

	divisor1 = NaturalDigits(b)[b->length - 1];
	divisor2 = 0;
	divisor3 = 0;
	if (b->length > 1)
	{
	    divisor2 = NaturalDigits(b)[b->length - 2];
	    if (b->length > 2)
		divisor3 = NaturalDigits(b)[b->length - 3];
	}
	/*
	 * Compute a scale factor to use as many bits of
	 * the divisor as possible, then scale the
	 * first and second digit of the divisor by that
	 * scale factor
	 */
	normal = normalize (divisor1);
	divisorc = divisor1 << normal;
	if (normal)
	    divisorc |= divisor2 >> (LBASE2 - normal);
	divisorc2 = divisor2 << normal;
	if (normal)
	    divisorc2 |= divisor3 >> (LBASE2 - normal);
#ifdef DEBUG
	printf ("divisor 1 %u divisor2 %u divisorc %u\n",
		(unsigned int) divisor1, (unsigned int) divisor2,
		(unsigned int) divisorc);
#endif
	/* 
	 * division just like humans, estimate each digit,
	 * correct by checking the partial product,
	 * then subtract the resultant product from
	 * the dividend (which has been copied into rem)
	 */
	for (index = remlen-1;
	     rem->length > 0 && index >= b->length;
	     index--)
	{
#ifdef DEBUG
	    prs ("rem:    ", rem);
	    prs ("b:      ", b);
#endif
	    /*
	     * estimate this digit
	     */
#ifdef DEBUG
	    printf ("digit1: %u digit2: %u divisorc: %u divisorc2: %u\n",
		    (unsigned int) NaturalDigits(rem)[index],
		    (unsigned int) NaturalDigits(rem)[index-1],
		    (unsigned int) divisorc,
		    (unsigned int) divisorc2);
#endif
	    /* 
	     * Using the scale factor and scaled divisor computed
	     * above, compute an estimate for this digit
	     *
	     * This is computing
	     *
	     *	    d1 = top dividend digit
	     *	    d2 = next dividend digit
	     *	    v1 = top divisor digit
	     *	    v2 = next divisor digit
	     *
	     *	      (d1 * base + d2)	<- original temp
	     * temp = ----------------
	     *        (v1 + v2 / base)	<- divisorc is scaled from this
	     *
	     * To make this computation work in integers,
	     * the top and bottom are scaled by the largest amount
	     * possible (base / (v1 + 1)); for powers of two, shift
	     * the divisor until the MSB is one.
	     */
	    dividend1 = NaturalDigits(rem)[index];
	    dividend2 = dividend3 = dividend4 = 0;
	    if (index)
	    {
		dividend2 = NaturalDigits(rem)[index - 1];
		if (index > 1)
		{
		    dividend3 = NaturalDigits(rem)[index - 2];
		    if (index > 2)
			dividend4 = NaturalDigits(rem)[index - 3];
		}
	    }
	    temp = (double_digit) dividend1 << LBASE2;
	    temp |= (double_digit) dividend2;
	    if (normal)
	    {
		temp = (temp << normal) | (dividend3 >> (LBASE2 - normal));
	    }
	    if ((digit) (temp >> LBASE2) == divisorc)
		d = (digit) 0xffffffffL;
	    else
		d = temp / divisorc;
#ifdef DEBUG	    
	    printf ("temp 0x%08x%08x divisorc 0x%08x d 0x%08x\n", 
		    (unsigned int) (temp >> 32), (unsigned int) temp,
		    divisorc, d);
#endif
	    offset = index - b->length;
	    if (d)
	    {
		temp2 = dividend3;
		if (normal)
		    temp2 = (temp2 << normal) | (dividend4 >> (LBASE2 - normal));
		temp = temp - (double_digit) d * (double_digit) divisorc;
		while (temp < BASE &&
		       (double_digit) divisorc2 * d > temp * BASE + temp2)
		{
		    --d;
		    temp += (double_digit) divisorc;
		}
		if (d == 1)
		{
		    carry = NaturalSubtractOffset (rem, b, offset);
		}
		else
		{
		    NaturalDigitMultiply (b, d, partial);
		    carry = NaturalSubtractOffset (rem, partial, offset);
		}
		if (carry)
		{
#ifdef DEBUG
		    printf ("add back 0x%x\n",
			    (unsigned int) d);
		    prs ("a", a);
		    prs ("b", b);
		    prs ("rem", rem);
		    prs ("partial", partial);
#endif
		    NaturalAddOffset (rem, b, offset);
#ifdef DEBUG
		    prs ("rem", rem);
#endif
		    d--;
		}
	    }
#ifdef DEBUG
	    prs ("partial", partial);
	    printf ("result[%d] = %u\n", (int) offset, 
		    (unsigned int) d);
#endif
	    NaturalDigits(quo)[offset] = d;
	}
	/*
	 * clean up the rest of the digits
	 */
	for (offset = index - b->length; offset >= 0; offset --) {
#ifdef DEBUG
	    printf ("result[%d] zeroed\n", offset);
#endif
	    NaturalDigits(quo)[offset] = 0;
	}
	for (index = quo->length - 1; index >= 0; index--)
	    if (NaturalDigits(quo)[index] != 0) {
		quo->length = index + 1;
		break;
	    }
#ifdef DEBUG
	prs ("quo: ", quo);
	prs ("rem: ", rem);
#endif
    }
#ifdef CHECK
    {
	Natural	*check;

	check = NaturalPlus (NaturalTimes (quo, b), rem);
	if (!NaturalEqual (check, a) ||
	    !NaturalLess (rem, b))
	{
	    prs ("a:     ", a);
	    prs ("b:     ", b);
	    prs ("quo:   ", quo);
	    prs ("rem:   ", rem);
	    prs ("check: ", check);
	    printf ("divide failed\n");
	}
    }
#endif
    EXIT ();
    /*
     * Stack guaratees that this will work -- it saves a place
     * for pushing at least as far as the previous StackReset, or
     * a stack chunk, whichever is smaller.
     */
    REFERENCE (quo);
    REFERENCE (rem);
    *remp = rem;
    return quo;
}