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