xref: /haiku/src/libs/mapm/mapm_div.c (revision 59d799dabcba86f92658ddb402f634e262d9aae7)
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