1*59d799daSIngo Weinhold
2*59d799daSIngo Weinhold /*
3*59d799daSIngo Weinhold * M_APM - mapm_div.c
4*59d799daSIngo Weinhold *
5*59d799daSIngo Weinhold * Copyright (C) 1999 - 2007 Michael C. Ring
6*59d799daSIngo Weinhold *
7*59d799daSIngo Weinhold * Permission to use, copy, and distribute this software and its
8*59d799daSIngo Weinhold * documentation for any purpose with or without fee is hereby granted,
9*59d799daSIngo Weinhold * provided that the above copyright notice appear in all copies and
10*59d799daSIngo Weinhold * that both that copyright notice and this permission notice appear
11*59d799daSIngo Weinhold * in supporting documentation.
12*59d799daSIngo Weinhold *
13*59d799daSIngo Weinhold * Permission to modify the software is granted. Permission to distribute
14*59d799daSIngo Weinhold * the modified code is granted. Modifications are to be distributed by
15*59d799daSIngo Weinhold * using the file 'license.txt' as a template to modify the file header.
16*59d799daSIngo Weinhold * 'license.txt' is available in the official MAPM distribution.
17*59d799daSIngo Weinhold *
18*59d799daSIngo Weinhold * This software is provided "as is" without express or implied warranty.
19*59d799daSIngo Weinhold */
20*59d799daSIngo Weinhold
21*59d799daSIngo Weinhold /*
22*59d799daSIngo Weinhold * $Id: mapm_div.c,v 1.12 2007/12/03 01:35:18 mike Exp $
23*59d799daSIngo Weinhold *
24*59d799daSIngo Weinhold * This file contains the basic division functions
25*59d799daSIngo Weinhold *
26*59d799daSIngo Weinhold * $Log: mapm_div.c,v $
27*59d799daSIngo Weinhold * Revision 1.12 2007/12/03 01:35:18 mike
28*59d799daSIngo Weinhold * Update license
29*59d799daSIngo Weinhold *
30*59d799daSIngo Weinhold * Revision 1.11 2003/07/21 20:07:13 mike
31*59d799daSIngo Weinhold * Modify error messages to be in a consistent format.
32*59d799daSIngo Weinhold *
33*59d799daSIngo Weinhold * Revision 1.10 2003/03/31 22:09:18 mike
34*59d799daSIngo Weinhold * call generic error handling function
35*59d799daSIngo Weinhold *
36*59d799daSIngo Weinhold * Revision 1.9 2002/11/03 22:06:50 mike
37*59d799daSIngo Weinhold * Updated function parameters to use the modern style
38*59d799daSIngo Weinhold *
39*59d799daSIngo Weinhold * Revision 1.8 2001/07/16 19:03:22 mike
40*59d799daSIngo Weinhold * add function M_free_all_div
41*59d799daSIngo Weinhold *
42*59d799daSIngo Weinhold * Revision 1.7 2001/02/11 22:30:42 mike
43*59d799daSIngo Weinhold * modify parameters to REALLOC
44*59d799daSIngo Weinhold *
45*59d799daSIngo Weinhold * Revision 1.6 2000/09/23 19:07:17 mike
46*59d799daSIngo Weinhold * change _divide to M_apm_sdivide function name
47*59d799daSIngo Weinhold *
48*59d799daSIngo Weinhold * Revision 1.5 2000/04/11 18:38:55 mike
49*59d799daSIngo Weinhold * use new algorithm to determine q-hat. uses more digits of
50*59d799daSIngo Weinhold * the numerator and denominator.
51*59d799daSIngo Weinhold *
52*59d799daSIngo Weinhold * Revision 1.4 2000/02/03 22:45:08 mike
53*59d799daSIngo Weinhold * use MAPM_* generic memory function
54*59d799daSIngo Weinhold *
55*59d799daSIngo Weinhold * Revision 1.3 1999/06/23 01:10:49 mike
56*59d799daSIngo Weinhold * use predefined constant for '15'
57*59d799daSIngo Weinhold *
58*59d799daSIngo Weinhold * Revision 1.2 1999/06/23 00:55:09 mike
59*59d799daSIngo Weinhold * change mult factor to 15
60*59d799daSIngo Weinhold *
61*59d799daSIngo Weinhold * Revision 1.1 1999/05/10 20:56:31 mike
62*59d799daSIngo Weinhold * Initial revision
63*59d799daSIngo Weinhold */
64*59d799daSIngo Weinhold
65*59d799daSIngo Weinhold #include "m_apm_lc.h"
66*59d799daSIngo Weinhold
67*59d799daSIngo Weinhold static M_APM M_div_worka;
68*59d799daSIngo Weinhold static M_APM M_div_workb;
69*59d799daSIngo Weinhold static M_APM M_div_tmp7;
70*59d799daSIngo Weinhold static M_APM M_div_tmp8;
71*59d799daSIngo Weinhold static M_APM M_div_tmp9;
72*59d799daSIngo Weinhold
73*59d799daSIngo Weinhold static int M_div_firsttime = TRUE;
74*59d799daSIngo Weinhold
75*59d799daSIngo Weinhold /****************************************************************************/
M_free_all_div()76*59d799daSIngo Weinhold void M_free_all_div()
77*59d799daSIngo Weinhold {
78*59d799daSIngo Weinhold if (M_div_firsttime == FALSE)
79*59d799daSIngo Weinhold {
80*59d799daSIngo Weinhold m_apm_free(M_div_worka);
81*59d799daSIngo Weinhold m_apm_free(M_div_workb);
82*59d799daSIngo Weinhold m_apm_free(M_div_tmp7);
83*59d799daSIngo Weinhold m_apm_free(M_div_tmp8);
84*59d799daSIngo Weinhold m_apm_free(M_div_tmp9);
85*59d799daSIngo Weinhold
86*59d799daSIngo Weinhold M_div_firsttime = TRUE;
87*59d799daSIngo Weinhold }
88*59d799daSIngo Weinhold }
89*59d799daSIngo Weinhold /****************************************************************************/
m_apm_integer_div_rem(M_APM qq,M_APM rr,M_APM aa,M_APM bb)90*59d799daSIngo Weinhold void m_apm_integer_div_rem(M_APM qq, M_APM rr, M_APM aa, M_APM bb)
91*59d799daSIngo Weinhold {
92*59d799daSIngo Weinhold m_apm_integer_divide(qq, aa, bb);
93*59d799daSIngo Weinhold m_apm_multiply(M_div_tmp7, qq, bb);
94*59d799daSIngo Weinhold m_apm_subtract(rr, aa, M_div_tmp7);
95*59d799daSIngo Weinhold }
96*59d799daSIngo Weinhold /****************************************************************************/
m_apm_integer_divide(M_APM rr,M_APM aa,M_APM bb)97*59d799daSIngo Weinhold void m_apm_integer_divide(M_APM rr, M_APM aa, M_APM bb)
98*59d799daSIngo Weinhold {
99*59d799daSIngo Weinhold /*
100*59d799daSIngo Weinhold * we must use this divide function since the
101*59d799daSIngo Weinhold * faster divide function using the reciprocal
102*59d799daSIngo Weinhold * will round the result (possibly changing
103*59d799daSIngo Weinhold * nnm.999999... --> nn(m+1).0000 which would
104*59d799daSIngo Weinhold * invalidate the 'integer_divide' goal).
105*59d799daSIngo Weinhold */
106*59d799daSIngo Weinhold
107*59d799daSIngo Weinhold M_apm_sdivide(rr, 4, aa, bb);
108*59d799daSIngo Weinhold
109*59d799daSIngo Weinhold if (rr->m_apm_exponent <= 0) /* result is 0 */
110*59d799daSIngo Weinhold {
111*59d799daSIngo Weinhold M_set_to_zero(rr);
112*59d799daSIngo Weinhold }
113*59d799daSIngo Weinhold else
114*59d799daSIngo Weinhold {
115*59d799daSIngo Weinhold if (rr->m_apm_datalength > rr->m_apm_exponent)
116*59d799daSIngo Weinhold {
117*59d799daSIngo Weinhold rr->m_apm_datalength = rr->m_apm_exponent;
118*59d799daSIngo Weinhold M_apm_normalize(rr);
119*59d799daSIngo Weinhold }
120*59d799daSIngo Weinhold }
121*59d799daSIngo Weinhold }
122*59d799daSIngo Weinhold /****************************************************************************/
M_apm_sdivide(M_APM r,int places,M_APM a,M_APM b)123*59d799daSIngo Weinhold void M_apm_sdivide(M_APM r, int places, M_APM a, M_APM b)
124*59d799daSIngo Weinhold {
125*59d799daSIngo Weinhold int j, k, m, b0, sign, nexp, indexr, icompare, iterations;
126*59d799daSIngo Weinhold long trial_numer;
127*59d799daSIngo Weinhold void *vp;
128*59d799daSIngo Weinhold
129*59d799daSIngo Weinhold if (M_div_firsttime)
130*59d799daSIngo Weinhold {
131*59d799daSIngo Weinhold M_div_firsttime = FALSE;
132*59d799daSIngo Weinhold
133*59d799daSIngo Weinhold M_div_worka = m_apm_init();
134*59d799daSIngo Weinhold M_div_workb = m_apm_init();
135*59d799daSIngo Weinhold M_div_tmp7 = m_apm_init();
136*59d799daSIngo Weinhold M_div_tmp8 = m_apm_init();
137*59d799daSIngo Weinhold M_div_tmp9 = m_apm_init();
138*59d799daSIngo Weinhold }
139*59d799daSIngo Weinhold
140*59d799daSIngo Weinhold sign = a->m_apm_sign * b->m_apm_sign;
141*59d799daSIngo Weinhold
142*59d799daSIngo Weinhold if (sign == 0) /* one number is zero, result is zero */
143*59d799daSIngo Weinhold {
144*59d799daSIngo Weinhold if (b->m_apm_sign == 0)
145*59d799daSIngo Weinhold {
146*59d799daSIngo Weinhold M_apm_log_error_msg(M_APM_RETURN, "\'M_apm_sdivide\', Divide by 0");
147*59d799daSIngo Weinhold }
148*59d799daSIngo Weinhold
149*59d799daSIngo Weinhold M_set_to_zero(r);
150*59d799daSIngo Weinhold return;
151*59d799daSIngo Weinhold }
152*59d799daSIngo Weinhold
153*59d799daSIngo Weinhold /*
154*59d799daSIngo Weinhold * Knuth step D1. Since base = 100, base / 2 = 50.
155*59d799daSIngo Weinhold * (also make the working copies positive)
156*59d799daSIngo Weinhold */
157*59d799daSIngo Weinhold
158*59d799daSIngo Weinhold if (b->m_apm_data[0] >= 50)
159*59d799daSIngo Weinhold {
160*59d799daSIngo Weinhold m_apm_absolute_value(M_div_worka, a);
161*59d799daSIngo Weinhold m_apm_absolute_value(M_div_workb, b);
162*59d799daSIngo Weinhold }
163*59d799daSIngo Weinhold else /* 'normal' step D1 */
164*59d799daSIngo Weinhold {
165*59d799daSIngo Weinhold k = 100 / (b->m_apm_data[0] + 1);
166*59d799daSIngo Weinhold m_apm_set_long(M_div_tmp9, (long)k);
167*59d799daSIngo Weinhold
168*59d799daSIngo Weinhold m_apm_multiply(M_div_worka, M_div_tmp9, a);
169*59d799daSIngo Weinhold m_apm_multiply(M_div_workb, M_div_tmp9, b);
170*59d799daSIngo Weinhold
171*59d799daSIngo Weinhold M_div_worka->m_apm_sign = 1;
172*59d799daSIngo Weinhold M_div_workb->m_apm_sign = 1;
173*59d799daSIngo Weinhold }
174*59d799daSIngo Weinhold
175*59d799daSIngo Weinhold /* setup trial denominator for step D3 */
176*59d799daSIngo Weinhold
177*59d799daSIngo Weinhold b0 = 100 * (int)M_div_workb->m_apm_data[0];
178*59d799daSIngo Weinhold
179*59d799daSIngo Weinhold if (M_div_workb->m_apm_datalength >= 3)
180*59d799daSIngo Weinhold b0 += M_div_workb->m_apm_data[1];
181*59d799daSIngo Weinhold
182*59d799daSIngo Weinhold nexp = M_div_worka->m_apm_exponent - M_div_workb->m_apm_exponent;
183*59d799daSIngo Weinhold
184*59d799daSIngo Weinhold if (nexp > 0)
185*59d799daSIngo Weinhold iterations = nexp + places + 1;
186*59d799daSIngo Weinhold else
187*59d799daSIngo Weinhold iterations = places + 1;
188*59d799daSIngo Weinhold
189*59d799daSIngo Weinhold k = (iterations + 1) >> 1; /* required size of result, in bytes */
190*59d799daSIngo Weinhold
191*59d799daSIngo Weinhold if (k > r->m_apm_malloclength)
192*59d799daSIngo Weinhold {
193*59d799daSIngo Weinhold if ((vp = MAPM_REALLOC(r->m_apm_data, (k + 32))) == NULL)
194*59d799daSIngo Weinhold {
195*59d799daSIngo Weinhold /* fatal, this does not return */
196*59d799daSIngo Weinhold
197*59d799daSIngo Weinhold M_apm_log_error_msg(M_APM_FATAL, "\'M_apm_sdivide\', Out of memory");
198*59d799daSIngo Weinhold }
199*59d799daSIngo Weinhold
200*59d799daSIngo Weinhold r->m_apm_malloclength = k + 28;
201*59d799daSIngo Weinhold r->m_apm_data = (UCHAR *)vp;
202*59d799daSIngo Weinhold }
203*59d799daSIngo Weinhold
204*59d799daSIngo Weinhold /* clear the exponent in the working copies */
205*59d799daSIngo Weinhold
206*59d799daSIngo Weinhold M_div_worka->m_apm_exponent = 0;
207*59d799daSIngo Weinhold M_div_workb->m_apm_exponent = 0;
208*59d799daSIngo Weinhold
209*59d799daSIngo Weinhold /* if numbers are equal, ratio == 1.00000... */
210*59d799daSIngo Weinhold
211*59d799daSIngo Weinhold if ((icompare = m_apm_compare(M_div_worka, M_div_workb)) == 0)
212*59d799daSIngo Weinhold {
213*59d799daSIngo Weinhold iterations = 1;
214*59d799daSIngo Weinhold r->m_apm_data[0] = 10;
215*59d799daSIngo Weinhold nexp++;
216*59d799daSIngo Weinhold }
217*59d799daSIngo Weinhold else /* ratio not 1, do the real division */
218*59d799daSIngo Weinhold {
219*59d799daSIngo Weinhold if (icompare == 1) /* numerator > denominator */
220*59d799daSIngo Weinhold {
221*59d799daSIngo Weinhold nexp++; /* to adjust the final exponent */
222*59d799daSIngo Weinhold M_div_worka->m_apm_exponent += 1; /* multiply numerator by 10 */
223*59d799daSIngo Weinhold }
224*59d799daSIngo Weinhold else /* numerator < denominator */
225*59d799daSIngo Weinhold {
226*59d799daSIngo Weinhold M_div_worka->m_apm_exponent += 2; /* multiply numerator by 100 */
227*59d799daSIngo Weinhold }
228*59d799daSIngo Weinhold
229*59d799daSIngo Weinhold indexr = 0;
230*59d799daSIngo Weinhold m = 0;
231*59d799daSIngo Weinhold
232*59d799daSIngo Weinhold while (TRUE)
233*59d799daSIngo Weinhold {
234*59d799daSIngo Weinhold /*
235*59d799daSIngo Weinhold * Knuth step D3. Only use the 3rd -> 6th digits if the number
236*59d799daSIngo Weinhold * actually has that many digits.
237*59d799daSIngo Weinhold */
238*59d799daSIngo Weinhold
239*59d799daSIngo Weinhold trial_numer = 10000L * (long)M_div_worka->m_apm_data[0];
240*59d799daSIngo Weinhold
241*59d799daSIngo Weinhold if (M_div_worka->m_apm_datalength >= 5)
242*59d799daSIngo Weinhold {
243*59d799daSIngo Weinhold trial_numer += 100 * M_div_worka->m_apm_data[1]
244*59d799daSIngo Weinhold + M_div_worka->m_apm_data[2];
245*59d799daSIngo Weinhold }
246*59d799daSIngo Weinhold else
247*59d799daSIngo Weinhold {
248*59d799daSIngo Weinhold if (M_div_worka->m_apm_datalength >= 3)
249*59d799daSIngo Weinhold trial_numer += 100 * M_div_worka->m_apm_data[1];
250*59d799daSIngo Weinhold }
251*59d799daSIngo Weinhold
252*59d799daSIngo Weinhold j = (int)(trial_numer / b0);
253*59d799daSIngo Weinhold
254*59d799daSIngo Weinhold /*
255*59d799daSIngo Weinhold * Since the library 'normalizes' all the results, we need
256*59d799daSIngo Weinhold * to look at the exponent of the number to decide if we
257*59d799daSIngo Weinhold * have a lead in 0n or 00.
258*59d799daSIngo Weinhold */
259*59d799daSIngo Weinhold
260*59d799daSIngo Weinhold if ((k = 2 - M_div_worka->m_apm_exponent) > 0)
261*59d799daSIngo Weinhold {
262*59d799daSIngo Weinhold while (TRUE)
263*59d799daSIngo Weinhold {
264*59d799daSIngo Weinhold j /= 10;
265*59d799daSIngo Weinhold if (--k == 0)
266*59d799daSIngo Weinhold break;
267*59d799daSIngo Weinhold }
268*59d799daSIngo Weinhold }
269*59d799daSIngo Weinhold
270*59d799daSIngo Weinhold if (j == 100) /* qhat == base ?? */
271*59d799daSIngo Weinhold j = 99; /* if so, decrease by 1 */
272*59d799daSIngo Weinhold
273*59d799daSIngo Weinhold m_apm_set_long(M_div_tmp8, (long)j);
274*59d799daSIngo Weinhold m_apm_multiply(M_div_tmp7, M_div_tmp8, M_div_workb);
275*59d799daSIngo Weinhold
276*59d799daSIngo Weinhold /*
277*59d799daSIngo Weinhold * Compare our q-hat (j) against the desired number.
278*59d799daSIngo Weinhold * j is either correct, 1 too large, or 2 too large
279*59d799daSIngo Weinhold * per Theorem B on pg 272 of Art of Compter Programming,
280*59d799daSIngo Weinhold * Volume 2, 3rd Edition.
281*59d799daSIngo Weinhold *
282*59d799daSIngo Weinhold * The above statement is only true if using the 2 leading
283*59d799daSIngo Weinhold * digits of the numerator and the leading digit of the
284*59d799daSIngo Weinhold * denominator. Since we are using the (3) leading digits
285*59d799daSIngo Weinhold * of the numerator and the (2) leading digits of the
286*59d799daSIngo Weinhold * denominator, we eliminate the case where our q-hat is
287*59d799daSIngo Weinhold * 2 too large, (and q-hat being 1 too large is quite remote).
288*59d799daSIngo Weinhold */
289*59d799daSIngo Weinhold
290*59d799daSIngo Weinhold if (m_apm_compare(M_div_tmp7, M_div_worka) == 1)
291*59d799daSIngo Weinhold {
292*59d799daSIngo Weinhold j--;
293*59d799daSIngo Weinhold m_apm_subtract(M_div_tmp8, M_div_tmp7, M_div_workb);
294*59d799daSIngo Weinhold m_apm_copy(M_div_tmp7, M_div_tmp8);
295*59d799daSIngo Weinhold }
296*59d799daSIngo Weinhold
297*59d799daSIngo Weinhold /*
298*59d799daSIngo Weinhold * Since we know q-hat is correct, step D6 is unnecessary.
299*59d799daSIngo Weinhold *
300*59d799daSIngo Weinhold * Store q-hat, step D5. Since D6 is unnecessary, we can
301*59d799daSIngo Weinhold * do D5 before D4 and decide if we are done.
302*59d799daSIngo Weinhold */
303*59d799daSIngo Weinhold
304*59d799daSIngo Weinhold r->m_apm_data[indexr++] = (UCHAR)j; /* j == 'qhat' */
305*59d799daSIngo Weinhold m += 2;
306*59d799daSIngo Weinhold
307*59d799daSIngo Weinhold if (m >= iterations)
308*59d799daSIngo Weinhold break;
309*59d799daSIngo Weinhold
310*59d799daSIngo Weinhold /* step D4 */
311*59d799daSIngo Weinhold
312*59d799daSIngo Weinhold m_apm_subtract(M_div_tmp9, M_div_worka, M_div_tmp7);
313*59d799daSIngo Weinhold
314*59d799daSIngo Weinhold /*
315*59d799daSIngo Weinhold * if the subtraction yields zero, the division is exact
316*59d799daSIngo Weinhold * and we are done early.
317*59d799daSIngo Weinhold */
318*59d799daSIngo Weinhold
319*59d799daSIngo Weinhold if (M_div_tmp9->m_apm_sign == 0)
320*59d799daSIngo Weinhold {
321*59d799daSIngo Weinhold iterations = m;
322*59d799daSIngo Weinhold break;
323*59d799daSIngo Weinhold }
324*59d799daSIngo Weinhold
325*59d799daSIngo Weinhold /* multiply by 100 and re-save */
326*59d799daSIngo Weinhold M_div_tmp9->m_apm_exponent += 2;
327*59d799daSIngo Weinhold m_apm_copy(M_div_worka, M_div_tmp9);
328*59d799daSIngo Weinhold }
329*59d799daSIngo Weinhold }
330*59d799daSIngo Weinhold
331*59d799daSIngo Weinhold r->m_apm_sign = sign;
332*59d799daSIngo Weinhold r->m_apm_exponent = nexp;
333*59d799daSIngo Weinhold r->m_apm_datalength = iterations;
334*59d799daSIngo Weinhold
335*59d799daSIngo Weinhold M_apm_normalize(r);
336*59d799daSIngo Weinhold }
337*59d799daSIngo Weinhold /****************************************************************************/
338