summaryrefslogtreecommitdiff
path: root/usr/src/lib/libmp/common/mdiv.c
blob: 1075a253cba54e9abcc98ae567a50867dbe4c836 (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
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
/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
/*	  All Rights Reserved  	*/


/*
 * Copyright (c) 1980 Regents of the University of California.
 * All rights reserved.  The Berkeley software License Agreement
 * specifies the terms and conditions for redistribution.
 */
/* 	Portions Copyright(c) 1988, Sun Microsystems Inc.	*/
/*	All Rights Reserved					*/

/*
 * Copyright (c) 1997, by Sun Microsystems, Inc.
 * All rights reserved.
 */

/*
 * Copyright (c) 2018, Joyent, Inc.
 */

/* LINTLIBRARY */

#include <mp.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include "libmp.h"

static void m_div(MINT *, MINT *, MINT *, MINT *);

void
mp_mdiv(MINT *a, MINT *b, MINT *q, MINT *r)
{
	MINT x, y;
	int sign;

	sign = 1;
	x.len = y.len = 0;
	_mp_move(a, &x);
	_mp_move(b, &y);
	if (x.len < 0) {
		sign = -1;
		x.len = -x.len;
	}
	if (y.len < 0) {
		sign = -sign;
		y.len = -y.len;
	}
	_mp_xfree(q);
	_mp_xfree(r);
	m_div(&x, &y, q, r);
	if (sign == -1) {
		q->len = -q->len;
		r->len = -r->len;
	}
	_mp_xfree(&x);
	_mp_xfree(&y);
}

static int
m_dsb(int qx, int n, short *a, short *b)
{
	int borrow;
	int s3b2shit;
	int j;
	short fifteen = 15;
	short *aptr, *bptr;
#ifdef DEBUGDSB
	(void) printf("m_dsb %d %d %d %d\n", qx, n, *a, *b);
#endif

	borrow = 0;
	aptr = a;
	bptr = b;
	for (j = n; j > 0; j--) {
#ifdef DEBUGDSB
		(void) printf("1 borrow=%x %d %d %d\n", borrow, (*aptr * qx),
		    *bptr, *aptr);
#endif
		borrow -= (*aptr++) * qx - *bptr;
#ifdef DEBUGDSB
		(void) printf("2 borrow=%x %d %d %d\n", borrow, (*aptr * qx),
		    *bptr, *aptr);
#endif
		*bptr++ = (short)(borrow & 077777);
#ifdef DEBUGDSB
		(void) printf("3 borrow=%x %d %d %d\n", borrow, (*aptr * qx),
		    *bptr, *aptr);
#endif
		if (borrow >= 0) borrow >>= fifteen; /* 3b2 */
		else borrow = 0xfffe0000 | (borrow >> fifteen);
#ifdef DEBUGDSB
		(void) printf("4 borrow=%x %d %d %d\n", borrow, (*aptr * qx),
		    *bptr, *aptr);
#endif
	}
	borrow += *bptr;
	*bptr = (short)(borrow & 077777);
	if (borrow >= 0) s3b2shit = borrow >> fifteen; /* 3b2 */
	else s3b2shit = 0xfffe0000 | (borrow >> fifteen);
	if (s3b2shit == 0) {
#ifdef DEBUGDSB
	(void) printf("mdsb 0\n");
#endif
		return (0);
	}
	borrow = 0;
	aptr = a;
	bptr = b;
	for (j = n; j > 0; j--) {
		borrow += *aptr++ + *bptr;
		*bptr++ = (short)(borrow & 077777);
		if (borrow >= 0) borrow >>= fifteen; /* 3b2 */
		else borrow = 0xfffe0000 | (borrow >>fifteen);
	}
#ifdef DEBUGDSB
	(void) printf("mdsb 1\n");
#endif
	return (1);
}

static int
m_trq(short v1, short v2, short u1, short u2, short u3)
{
	short d;
	int x1;
	int c1;

	c1 = u1 * 0100000 + u2;
	if (u1 == v1) {
		d = 077777;
	} else {
		d = (short)(c1 / v1);
	}
	do {
		x1 = c1 - v1 * d;
		x1 = x1 * 0100000 + u3 - v2 * d;
		--d;
	} while (x1 < 0);
#ifdef DEBUGMTRQ
	(void) printf("mtrq %d %d %d %d %d %d\n", v1, v2, u1, u2, u3, (d+1));
#endif
	return ((int)d + 1);
}

static void
m_div(MINT *a, MINT *b, MINT *q, MINT *r)
{
	MINT u, v, x, w;
	short d;
	short *qval;
	short *uval;
	int j;
	int qq;
	int n;
	short v1;
	short v2;

	u.len = v.len = x.len = w.len = 0;
	if (b->len == 0) {
		_mp_fatal("mdiv divide by zero");
		return;
	}
	if (b->len == 1) {
		r->val = _mp_xalloc(1, "m_div1");
		mp_sdiv(a, b->val[0], q, r->val);
		if (r->val[0] == 0) {
			free(r->val);
			r->len = 0;
		} else {
			r->len = 1;
		}
		return;
	}
	if (a -> len < b -> len) {
		q->len = 0;
		r->len = a->len;
		r->val = _mp_xalloc(r->len, "m_div2");
		for (qq = 0; qq < r->len; qq++) {
			r->val[qq] = a->val[qq];
		}
		return;
	}
	x.len = 1;
	x.val = &d;
	n = b->len;
	d = 0100000 / (b->val[n - 1] + 1);
	mp_mult(a, &x, &u); /* subtle: relies on mult allocing extra space */
	mp_mult(b, &x, &v);
#ifdef DEBUG_MDIV
	(void) printf("  u=%s\n", mtox(&u));
	(void) printf("  v=%s\n", mtox(&v));
#endif
	v1 = v.val[n - 1];
	v2 = v.val[n - 2];
	qval = _mp_xalloc(a -> len - n + 1, "m_div3");
	uval = u.val;
	for (j = a->len - n; j >= 0; j--) {
		qq = m_trq(v1, v2, uval[j + n], uval[j + n - 1],
							uval[j + n - 2]);
		if (m_dsb(qq, n, v.val, uval + j))
			qq -= 1;
		qval[j] = (short)qq;
	}
	x.len = n;
	x.val = u.val;
	_mp_mcan(&x);
#ifdef DEBUG_MDIV
	(void) printf("  x=%s\n", mtox(&x));
	(void) printf("  d(in)=%d\n", (d));
#endif
	mp_sdiv(&x, d, &w, &d);
#ifdef DEBUG_MDIV
	(void) printf("  w=%s\n", mtox(&w));
	(void) printf("  d(out)=%d\n", (d));
#endif
	r->len = w.len;
	r->val = w.val;
	q->val = qval;
	qq = a->len - n + 1;
	if (qq > 0 && qval[qq - 1] == 0)
		qq -= 1;
	q->len = qq;
	if (qq == 0)
		free(qval);
	if (x.len != 0)
		_mp_xfree(&u);
	_mp_xfree(&v);
}