xref: /haiku/src/libs/mapm/mapmfact.c (revision 59d799dabcba86f92658ddb402f634e262d9aae7)
1*59d799daSIngo Weinhold 
2*59d799daSIngo Weinhold /*
3*59d799daSIngo Weinhold  *  M_APM  -  mapmfact.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: mapmfact.c,v 1.11 2007/12/03 01:51:50 mike Exp $
23*59d799daSIngo Weinhold  *
24*59d799daSIngo Weinhold  *      This file contains the FACTORIAL function.
25*59d799daSIngo Weinhold  *
26*59d799daSIngo Weinhold  *      $Log: mapmfact.c,v $
27*59d799daSIngo Weinhold  *      Revision 1.11  2007/12/03 01:51:50  mike
28*59d799daSIngo Weinhold  *      Update license
29*59d799daSIngo Weinhold  *
30*59d799daSIngo Weinhold  *      Revision 1.10  2003/07/21 21:05:31  mike
31*59d799daSIngo Weinhold  *      facilitate 'gcov' code coverage tool by making an array smaller
32*59d799daSIngo Weinhold  *
33*59d799daSIngo Weinhold  *      Revision 1.9  2002/11/03 21:27:28  mike
34*59d799daSIngo Weinhold  *      Updated function parameters to use the modern style
35*59d799daSIngo Weinhold  *
36*59d799daSIngo Weinhold  *      Revision 1.8  2000/06/14 20:36:16  mike
37*59d799daSIngo Weinhold  *      increase size of DOS array
38*59d799daSIngo Weinhold  *
39*59d799daSIngo Weinhold  *      Revision 1.7  2000/05/29 13:15:59  mike
40*59d799daSIngo Weinhold  *      minor tweaks, fixed comment
41*59d799daSIngo Weinhold  *
42*59d799daSIngo Weinhold  *      Revision 1.6  2000/05/26 16:39:03  mike
43*59d799daSIngo Weinhold  *      minor update to comments and code
44*59d799daSIngo Weinhold  *
45*59d799daSIngo Weinhold  *      Revision 1.5  2000/05/25 23:12:53  mike
46*59d799daSIngo Weinhold  *      change 'nd' calculation
47*59d799daSIngo Weinhold  *
48*59d799daSIngo Weinhold  *      Revision 1.4  2000/05/25 22:17:45  mike
49*59d799daSIngo Weinhold  *      implement new algorithm for speed. approx 5 - 10X
50*59d799daSIngo Weinhold  *      faster on my PC when N! is large (> 6000)
51*59d799daSIngo Weinhold  *
52*59d799daSIngo Weinhold  *      Revision 1.3  1999/06/19 21:25:21  mike
53*59d799daSIngo Weinhold  *      changed local static variables to MAPM stack variables
54*59d799daSIngo Weinhold  *
55*59d799daSIngo Weinhold  *      Revision 1.2  1999/05/23 18:21:12  mike
56*59d799daSIngo Weinhold  *      minor variable name tweaks
57*59d799daSIngo Weinhold  *
58*59d799daSIngo Weinhold  *      Revision 1.1  1999/05/15 21:06:11  mike
59*59d799daSIngo Weinhold  *      Initial revision
60*59d799daSIngo Weinhold  */
61*59d799daSIngo Weinhold 
62*59d799daSIngo Weinhold /*
63*59d799daSIngo Weinhold  *      Brief explanation of the factorial algorithm.
64*59d799daSIngo Weinhold  *      ----------------------------------------------
65*59d799daSIngo Weinhold  *
66*59d799daSIngo Weinhold  *      The old algorithm simply multiplied N * (N-1) * (N-2) etc, until
67*59d799daSIngo Weinhold  *	the number counted down to '2'. So one term of the multiplication
68*59d799daSIngo Weinhold  *	kept getting bigger while multiplying by the next number in the
69*59d799daSIngo Weinhold  *	sequence.
70*59d799daSIngo Weinhold  *
71*59d799daSIngo Weinhold  *      The new algorithm takes advantage of the fast multiplication
72*59d799daSIngo Weinhold  *	algorithm. The "ideal" setup for fast multiplication is when
73*59d799daSIngo Weinhold  *	both numbers have approx the same number of significant digits
74*59d799daSIngo Weinhold  *	and the number of digits is very near (but not over) an exact
75*59d799daSIngo Weinhold  *	power of 2.
76*59d799daSIngo Weinhold  *
77*59d799daSIngo Weinhold  *	So, we will multiply N * (N-1) * (N-2), etc until the number of
78*59d799daSIngo Weinhold  *	significant digits is approx 256.
79*59d799daSIngo Weinhold  *
80*59d799daSIngo Weinhold  *	Store this temp product into an array.
81*59d799daSIngo Weinhold  *
82*59d799daSIngo Weinhold  *	Then we will multiply the next sequence until the number of
83*59d799daSIngo Weinhold  *	significant digits is approx 256.
84*59d799daSIngo Weinhold  *
85*59d799daSIngo Weinhold  *	Store this temp product into the next element of the array.
86*59d799daSIngo Weinhold  *
87*59d799daSIngo Weinhold  *	Continue until we've counted down to 2.
88*59d799daSIngo Weinhold  *
89*59d799daSIngo Weinhold  *	We now have an array of numbers with approx the same number
90*59d799daSIngo Weinhold  *	of digits (except for the last element, depending on where it
91*59d799daSIngo Weinhold  *	ended.) Now multiply each of the array elements together to
92*59d799daSIngo Weinhold  *	get the final product.
93*59d799daSIngo Weinhold  *
94*59d799daSIngo Weinhold  *      The array multiplies are done as follows (assume we used 11
95*59d799daSIngo Weinhold  *	array elements for this example, indicated by [0] - [10] ) :
96*59d799daSIngo Weinhold  *
97*59d799daSIngo Weinhold  *	initial    iter-1     iter-2       iter-3     iter-4
98*59d799daSIngo Weinhold  *
99*59d799daSIngo Weinhold  *	  [0]
100*59d799daSIngo Weinhold  *	     *  ->  [0]
101*59d799daSIngo Weinhold  *	  [1]
102*59d799daSIngo Weinhold  *                      * ->    [0]
103*59d799daSIngo Weinhold  *
104*59d799daSIngo Weinhold  *	  [2]
105*59d799daSIngo Weinhold  *	     *  ->  [1]
106*59d799daSIngo Weinhold  *	  [3]
107*59d799daSIngo Weinhold  *                                   * ->   [0]
108*59d799daSIngo Weinhold  *
109*59d799daSIngo Weinhold  *	  [4]
110*59d799daSIngo Weinhold  *	     *  ->  [2]
111*59d799daSIngo Weinhold  *	  [5]
112*59d799daSIngo Weinhold  *
113*59d799daSIngo Weinhold  *                      * ->    [1]
114*59d799daSIngo Weinhold  *
115*59d799daSIngo Weinhold  *	  [6]
116*59d799daSIngo Weinhold  *	     *  ->  [3]                           *  ->  [0]
117*59d799daSIngo Weinhold  *	  [7]
118*59d799daSIngo Weinhold  *
119*59d799daSIngo Weinhold  *
120*59d799daSIngo Weinhold  *	  [8]
121*59d799daSIngo Weinhold  *	     *  ->  [4]
122*59d799daSIngo Weinhold  *	  [9]
123*59d799daSIngo Weinhold  *                      * ->    [2]    ->   [1]
124*59d799daSIngo Weinhold  *
125*59d799daSIngo Weinhold  *
126*59d799daSIngo Weinhold  *	  [10]  ->  [5]
127*59d799daSIngo Weinhold  *
128*59d799daSIngo Weinhold  */
129*59d799daSIngo Weinhold 
130*59d799daSIngo Weinhold #include "m_apm_lc.h"
131*59d799daSIngo Weinhold 
132*59d799daSIngo Weinhold /* define size of local array for temp storage */
133*59d799daSIngo Weinhold 
134*59d799daSIngo Weinhold #define NDIM 32
135*59d799daSIngo Weinhold 
136*59d799daSIngo Weinhold /****************************************************************************/
m_apm_factorial(M_APM moutput,M_APM minput)137*59d799daSIngo Weinhold void	m_apm_factorial(M_APM moutput, M_APM minput)
138*59d799daSIngo Weinhold {
139*59d799daSIngo Weinhold int     ii, nmul, ndigits, nd, jj, kk, mm, ct;
140*59d799daSIngo Weinhold M_APM   array[NDIM];
141*59d799daSIngo Weinhold M_APM   iprod1, iprod2, tmp1, tmp2;
142*59d799daSIngo Weinhold 
143*59d799daSIngo Weinhold /* return 1 for any input <= 1 */
144*59d799daSIngo Weinhold 
145*59d799daSIngo Weinhold if (m_apm_compare(minput, MM_One) <= 0)
146*59d799daSIngo Weinhold   {
147*59d799daSIngo Weinhold    m_apm_copy(moutput, MM_One);
148*59d799daSIngo Weinhold    return;
149*59d799daSIngo Weinhold   }
150*59d799daSIngo Weinhold 
151*59d799daSIngo Weinhold ct       = 0;
152*59d799daSIngo Weinhold mm       = NDIM - 2;
153*59d799daSIngo Weinhold ndigits  = 256;
154*59d799daSIngo Weinhold nd       = ndigits - 20;
155*59d799daSIngo Weinhold tmp1     = m_apm_init();
156*59d799daSIngo Weinhold tmp2     = m_apm_init();
157*59d799daSIngo Weinhold iprod1   = m_apm_init();
158*59d799daSIngo Weinhold iprod2   = m_apm_init();
159*59d799daSIngo Weinhold array[0] = m_apm_init();
160*59d799daSIngo Weinhold 
161*59d799daSIngo Weinhold m_apm_copy(tmp2, minput);
162*59d799daSIngo Weinhold 
163*59d799daSIngo Weinhold /* loop until multiply count-down has reached '2' */
164*59d799daSIngo Weinhold 
165*59d799daSIngo Weinhold while (TRUE)
166*59d799daSIngo Weinhold   {
167*59d799daSIngo Weinhold    m_apm_copy(iprod1, MM_One);
168*59d799daSIngo Weinhold 
169*59d799daSIngo Weinhold    /*
170*59d799daSIngo Weinhold     *   loop until the number of significant digits in this
171*59d799daSIngo Weinhold     *   partial result is slightly less than 256
172*59d799daSIngo Weinhold     */
173*59d799daSIngo Weinhold 
174*59d799daSIngo Weinhold    while (TRUE)
175*59d799daSIngo Weinhold      {
176*59d799daSIngo Weinhold       m_apm_multiply(iprod2, iprod1, tmp2);
177*59d799daSIngo Weinhold 
178*59d799daSIngo Weinhold       m_apm_subtract(tmp1, tmp2, MM_One);
179*59d799daSIngo Weinhold 
180*59d799daSIngo Weinhold       m_apm_multiply(iprod1, iprod2, tmp1);
181*59d799daSIngo Weinhold 
182*59d799daSIngo Weinhold       /*
183*59d799daSIngo Weinhold        *  I know, I know.  There just isn't a *clean* way
184*59d799daSIngo Weinhold        *  to break out of 2 nested loops.
185*59d799daSIngo Weinhold        */
186*59d799daSIngo Weinhold 
187*59d799daSIngo Weinhold       if (m_apm_compare(tmp1, MM_Two) <= 0)
188*59d799daSIngo Weinhold         goto PHASE2;
189*59d799daSIngo Weinhold 
190*59d799daSIngo Weinhold       m_apm_subtract(tmp2, tmp1, MM_One);
191*59d799daSIngo Weinhold 
192*59d799daSIngo Weinhold       if (iprod1->m_apm_datalength > nd)
193*59d799daSIngo Weinhold         break;
194*59d799daSIngo Weinhold      }
195*59d799daSIngo Weinhold 
196*59d799daSIngo Weinhold    if (ct == (NDIM - 1))
197*59d799daSIngo Weinhold      {
198*59d799daSIngo Weinhold       /*
199*59d799daSIngo Weinhold        *    if the array has filled up, start multiplying
200*59d799daSIngo Weinhold        *    some of the partial products now.
201*59d799daSIngo Weinhold        */
202*59d799daSIngo Weinhold 
203*59d799daSIngo Weinhold       m_apm_copy(tmp1, array[mm]);
204*59d799daSIngo Weinhold       m_apm_multiply(array[mm], iprod1, tmp1);
205*59d799daSIngo Weinhold 
206*59d799daSIngo Weinhold       if (mm == 0)
207*59d799daSIngo Weinhold         {
208*59d799daSIngo Weinhold          mm = NDIM - 2;
209*59d799daSIngo Weinhold 	 ndigits = ndigits << 1;
210*59d799daSIngo Weinhold          nd = ndigits - 20;
211*59d799daSIngo Weinhold 	}
212*59d799daSIngo Weinhold       else
213*59d799daSIngo Weinhold          mm--;
214*59d799daSIngo Weinhold      }
215*59d799daSIngo Weinhold    else
216*59d799daSIngo Weinhold      {
217*59d799daSIngo Weinhold       /*
218*59d799daSIngo Weinhold        *    store this partial product in the array
219*59d799daSIngo Weinhold        *    and allocate the next array element
220*59d799daSIngo Weinhold        */
221*59d799daSIngo Weinhold 
222*59d799daSIngo Weinhold       m_apm_copy(array[ct], iprod1);
223*59d799daSIngo Weinhold       array[++ct] = m_apm_init();
224*59d799daSIngo Weinhold      }
225*59d799daSIngo Weinhold   }
226*59d799daSIngo Weinhold 
227*59d799daSIngo Weinhold PHASE2:
228*59d799daSIngo Weinhold 
229*59d799daSIngo Weinhold m_apm_copy(array[ct], iprod1);
230*59d799daSIngo Weinhold 
231*59d799daSIngo Weinhold kk = ct;
232*59d799daSIngo Weinhold 
233*59d799daSIngo Weinhold while (kk != 0)
234*59d799daSIngo Weinhold   {
235*59d799daSIngo Weinhold    ii = 0;
236*59d799daSIngo Weinhold    jj = 0;
237*59d799daSIngo Weinhold    nmul = (kk + 1) >> 1;
238*59d799daSIngo Weinhold 
239*59d799daSIngo Weinhold    while (TRUE)
240*59d799daSIngo Weinhold      {
241*59d799daSIngo Weinhold       /* must use tmp var when ii,jj point to same element */
242*59d799daSIngo Weinhold 
243*59d799daSIngo Weinhold       if (ii == 0)
244*59d799daSIngo Weinhold         {
245*59d799daSIngo Weinhold          m_apm_copy(tmp1, array[ii]);
246*59d799daSIngo Weinhold          m_apm_multiply(array[jj], tmp1, array[ii+1]);
247*59d799daSIngo Weinhold         }
248*59d799daSIngo Weinhold       else
249*59d799daSIngo Weinhold          m_apm_multiply(array[jj], array[ii], array[ii+1]);
250*59d799daSIngo Weinhold 
251*59d799daSIngo Weinhold       if (++jj == nmul)
252*59d799daSIngo Weinhold         break;
253*59d799daSIngo Weinhold 
254*59d799daSIngo Weinhold       ii += 2;
255*59d799daSIngo Weinhold      }
256*59d799daSIngo Weinhold 
257*59d799daSIngo Weinhold    if ((kk & 1) == 0)
258*59d799daSIngo Weinhold      {
259*59d799daSIngo Weinhold       jj = kk >> 1;
260*59d799daSIngo Weinhold       m_apm_copy(array[jj], array[kk]);
261*59d799daSIngo Weinhold      }
262*59d799daSIngo Weinhold 
263*59d799daSIngo Weinhold    kk = kk >> 1;
264*59d799daSIngo Weinhold   }
265*59d799daSIngo Weinhold 
266*59d799daSIngo Weinhold m_apm_copy(moutput, array[0]);
267*59d799daSIngo Weinhold 
268*59d799daSIngo Weinhold for (ii=0; ii <= ct; ii++)
269*59d799daSIngo Weinhold   {
270*59d799daSIngo Weinhold    m_apm_free(array[ii]);
271*59d799daSIngo Weinhold   }
272*59d799daSIngo Weinhold 
273*59d799daSIngo Weinhold m_apm_free(tmp1);
274*59d799daSIngo Weinhold m_apm_free(tmp2);
275*59d799daSIngo Weinhold m_apm_free(iprod1);
276*59d799daSIngo Weinhold m_apm_free(iprod2);
277*59d799daSIngo Weinhold }
278*59d799daSIngo Weinhold /****************************************************************************/
279