File: mapm_div.cpp

package info (click to toggle)
pgadmin3 1.20.0~beta2-1
  • links: PTS, VCS
  • area: main
  • in suites: jessie, jessie-kfreebsd
  • size: 73,704 kB
  • ctags: 18,591
  • sloc: cpp: 193,786; ansic: 18,736; sh: 5,154; pascal: 1,120; yacc: 927; makefile: 516; lex: 421; xml: 126; perl: 40
file content (300 lines) | stat: -rw-r--r-- 7,576 bytes parent folder | download | duplicates (5)
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
293
294
295
296
297
298
299
300

/*
 *  M_APM  -  mapm_div.c
 *
 *  Copyright (C) 1999 - 2007   Michael C. Ring
 *
 *  Permission to use, copy, and distribute this software and its
 *  documentation for any purpose with or without fee is hereby granted,
 *  provided that the above copyright notice appear in all copies and
 *  that both that copyright notice and this permission notice appear
 *  in supporting documentation.
 *
 *  Permission to modify the software is granted. Permission to distribute
 *  the modified code is granted. Modifications are to be distributed by
 *  using the file 'license.txt' as a template to modify the file header.
 *  'license.txt' is available in the official MAPM distribution.
 *
 *  This software is provided "as is" without express or implied warranty.
 */

/*
 *
 *      This file contains the basic division functions
 *
 */

#include "pgAdmin3.h"
#include "pgscript/utilities/mapm-lib/m_apm_lc.h"

static	M_APM	M_div_worka;
static	M_APM	M_div_workb;
static	M_APM	M_div_tmp7;
static	M_APM	M_div_tmp8;
static	M_APM	M_div_tmp9;

static	int	M_div_firsttime = TRUE;

/****************************************************************************/
void	M_free_all_div()
{
	if (M_div_firsttime == FALSE)
	{
		m_apm_free(M_div_worka);
		m_apm_free(M_div_workb);
		m_apm_free(M_div_tmp7);
		m_apm_free(M_div_tmp8);
		m_apm_free(M_div_tmp9);

		M_div_firsttime = TRUE;
	}
}
/****************************************************************************/
void	m_apm_integer_div_rem(M_APM qq, M_APM rr, M_APM aa, M_APM bb)
{
	m_apm_integer_divide(qq, aa, bb);
	m_apm_multiply(M_div_tmp7, qq, bb);
	m_apm_subtract(rr, aa, M_div_tmp7);
}
/****************************************************************************/
void	m_apm_integer_divide(M_APM rr, M_APM aa, M_APM bb)
{
	/*
	 *    we must use this divide function since the
	 *    faster divide function using the reciprocal
	 *    will round the result (possibly changing
	 *    nnm.999999...  -->  nn(m+1).0000 which would
	 *    invalidate the 'integer_divide' goal).
	 */

	M_apm_sdivide(rr, 4, aa, bb);

	if (rr->m_apm_exponent <= 0)        /* result is 0 */
	{
		M_set_to_zero(rr);
	}
	else
	{
		if (rr->m_apm_datalength > rr->m_apm_exponent)
		{
			rr->m_apm_datalength = rr->m_apm_exponent;
			M_apm_normalize(rr);
		}
	}
}
/****************************************************************************/
void	M_apm_sdivide(M_APM r, int places, M_APM a, M_APM b)
{
	int	j, k, m, b0, sign, nexp, indexr, icompare, iterations;
	long    trial_numer;
	void	*vp;

	if (M_div_firsttime)
	{
		M_div_firsttime = FALSE;

		M_div_worka = m_apm_init();
		M_div_workb = m_apm_init();
		M_div_tmp7  = m_apm_init();
		M_div_tmp8  = m_apm_init();
		M_div_tmp9  = m_apm_init();
	}

	sign = a->m_apm_sign * b->m_apm_sign;

	if (sign == 0)      /* one number is zero, result is zero */
	{
		if (b->m_apm_sign == 0)
		{
			M_apm_log_error_msg(M_APM_RETURN, "\'M_apm_sdivide\', Divide by 0");
		}

		M_set_to_zero(r);
		return;
	}

	/*
	 *  Knuth step D1. Since base = 100, base / 2 = 50.
	 *  (also make the working copies positive)
	 */

	if (b->m_apm_data[0] >= 50)
	{
		m_apm_absolute_value(M_div_worka, a);
		m_apm_absolute_value(M_div_workb, b);
	}
	else       /* 'normal' step D1 */
	{
		k = 100 / (b->m_apm_data[0] + 1);
		m_apm_set_long(M_div_tmp9, (long)k);

		m_apm_multiply(M_div_worka, M_div_tmp9, a);
		m_apm_multiply(M_div_workb, M_div_tmp9, b);

		M_div_worka->m_apm_sign = 1;
		M_div_workb->m_apm_sign = 1;
	}

	/* setup trial denominator for step D3 */

	b0 = 100 * (int)M_div_workb->m_apm_data[0];

	if (M_div_workb->m_apm_datalength >= 3)
		b0 += M_div_workb->m_apm_data[1];

	nexp = M_div_worka->m_apm_exponent - M_div_workb->m_apm_exponent;

	if (nexp > 0)
		iterations = nexp + places + 1;
	else
		iterations = places + 1;

	k = (iterations + 1) >> 1;     /* required size of result, in bytes */

	if (k > r->m_apm_malloclength)
	{
		if ((vp = MAPM_REALLOC(r->m_apm_data, (k + 32))) == NULL)
		{
			/* fatal, this does not return */

			M_apm_log_error_msg(M_APM_FATAL, "\'M_apm_sdivide\', Out of memory");
		}

		r->m_apm_malloclength = k + 28;
		r->m_apm_data = (UCHAR *)vp;
	}

	/* clear the exponent in the working copies */

	M_div_worka->m_apm_exponent = 0;
	M_div_workb->m_apm_exponent = 0;

	/* if numbers are equal, ratio == 1.00000... */

	if ((icompare = m_apm_compare(M_div_worka, M_div_workb)) == 0)
	{
		iterations = 1;
		r->m_apm_data[0] = 10;
		nexp++;
	}
	else			           /* ratio not 1, do the real division */
	{
		if (icompare == 1)                        /* numerator > denominator */
		{
			nexp++;                           /* to adjust the final exponent */
			M_div_worka->m_apm_exponent += 1;     /* multiply numerator by 10 */
		}
		else                                      /* numerator < denominator */
		{
			M_div_worka->m_apm_exponent += 2;    /* multiply numerator by 100 */
		}

		indexr = 0;
		m      = 0;

		while (TRUE)
		{
			/*
			 *  Knuth step D3. Only use the 3rd -> 6th digits if the number
			 *  actually has that many digits.
			 */

			trial_numer = 10000L * (long)M_div_worka->m_apm_data[0];

			if (M_div_worka->m_apm_datalength >= 5)
			{
				trial_numer += 100 * M_div_worka->m_apm_data[1]
				               + M_div_worka->m_apm_data[2];
			}
			else
			{
				if (M_div_worka->m_apm_datalength >= 3)
					trial_numer += 100 * M_div_worka->m_apm_data[1];
			}

			j = (int)(trial_numer / b0);

			/*
			 *    Since the library 'normalizes' all the results, we need
			 *    to look at the exponent of the number to decide if we
			 *    have a lead in 0n or 00.
			 */

			if ((k = 2 - M_div_worka->m_apm_exponent) > 0)
			{
				while (TRUE)
				{
					j /= 10;
					if (--k == 0)
						break;
				}
			}

			if (j == 100)     /* qhat == base ??      */
				j = 99;         /* if so, decrease by 1 */

			m_apm_set_long(M_div_tmp8, (long)j);
			m_apm_multiply(M_div_tmp7, M_div_tmp8, M_div_workb);

			/*
			 *    Compare our q-hat (j) against the desired number.
			 *    j is either correct, 1 too large, or 2 too large
			 *    per Theorem B on pg 272 of Art of Compter Programming,
			 *    Volume 2, 3rd Edition.
			 *
			 *    The above statement is only true if using the 2 leading
			 *    digits of the numerator and the leading digit of the
			 *    denominator. Since we are using the (3) leading digits
			 *    of the numerator and the (2) leading digits of the
			 *    denominator, we eliminate the case where our q-hat is
			 *    2 too large, (and q-hat being 1 too large is quite remote).
			 */

			if (m_apm_compare(M_div_tmp7, M_div_worka) == 1)
			{
				j--;
				m_apm_subtract(M_div_tmp8, M_div_tmp7, M_div_workb);
				m_apm_copy(M_div_tmp7, M_div_tmp8);
			}

			/*
			 *  Since we know q-hat is correct, step D6 is unnecessary.
			 *
			 *  Store q-hat, step D5. Since D6 is unnecessary, we can
			 *  do D5 before D4 and decide if we are done.
			 */

			r->m_apm_data[indexr++] = (UCHAR)j;    /* j == 'qhat' */
			m += 2;

			if (m >= iterations)
				break;

			/* step D4 */

			m_apm_subtract(M_div_tmp9, M_div_worka, M_div_tmp7);

			/*
			 *  if the subtraction yields zero, the division is exact
			 *  and we are done early.
			 */

			if (M_div_tmp9->m_apm_sign == 0)
			{
				iterations = m;
				break;
			}

			/* multiply by 100 and re-save */
			M_div_tmp9->m_apm_exponent += 2;
			m_apm_copy(M_div_worka, M_div_tmp9);
		}
	}

	r->m_apm_sign       = sign;
	r->m_apm_exponent   = nexp;
	r->m_apm_datalength = iterations;

	M_apm_normalize(r);
}
/****************************************************************************/