5
* Copyright (C) 1999 - 2007 Michael C. Ring
7
* Permission to use, copy, and distribute this software and its
8
* documentation for any purpose with or without fee is hereby granted,
9
* provided that the above copyright notice appear in all copies and
10
* that both that copyright notice and this permission notice appear
11
* in supporting documentation.
13
* Permission to modify the software is granted. Permission to distribute
14
* the modified code is granted. Modifications are to be distributed by
15
* using the file 'license.txt' as a template to modify the file header.
16
* 'license.txt' is available in the official MAPM distribution.
18
* This software is provided "as is" without express or implied warranty.
23
* This file contains the FACTORIAL function.
28
* Brief explanation of the factorial algorithm.
29
* ----------------------------------------------
31
* The old algorithm simply multiplied N * (N-1) * (N-2) etc, until
32
* the number counted down to '2'. So one term of the multiplication
33
* kept getting bigger while multiplying by the next number in the
36
* The new algorithm takes advantage of the fast multiplication
37
* algorithm. The "ideal" setup for fast multiplication is when
38
* both numbers have approx the same number of significant digits
39
* and the number of digits is very near (but not over) an exact
42
* So, we will multiply N * (N-1) * (N-2), etc until the number of
43
* significant digits is approx 256.
45
* Store this temp product into an array.
47
* Then we will multiply the next sequence until the number of
48
* significant digits is approx 256.
50
* Store this temp product into the next element of the array.
52
* Continue until we've counted down to 2.
54
* We now have an array of numbers with approx the same number
55
* of digits (except for the last element, depending on where it
56
* ended.) Now multiply each of the array elements together to
57
* get the final product.
59
* The array multiplies are done as follows (assume we used 11
60
* array elements for this example, indicated by [0] - [10] ) :
62
* initial iter-1 iter-2 iter-3 iter-4
96
#include "pgscript/utilities/mapm-lib/m_apm_lc.h"
98
/* define size of local array for temp storage */
102
/****************************************************************************/
103
void m_apm_factorial(M_APM moutput, M_APM minput)
105
int ii, nmul, ndigits, nd, jj, kk, mm, ct;
107
M_APM iprod1, iprod2, tmp1, tmp2;
109
/* return 1 for any input <= 1 */
111
if (m_apm_compare(minput, MM_One) <= 0)
113
m_apm_copy(moutput, MM_One);
123
iprod1 = m_apm_init();
124
iprod2 = m_apm_init();
125
array[0] = m_apm_init();
127
m_apm_copy(tmp2, minput);
129
/* loop until multiply count-down has reached '2' */
133
m_apm_copy(iprod1, MM_One);
136
* loop until the number of significant digits in this
137
* partial result is slightly less than 256
142
m_apm_multiply(iprod2, iprod1, tmp2);
144
m_apm_subtract(tmp1, tmp2, MM_One);
146
m_apm_multiply(iprod1, iprod2, tmp1);
149
* I know, I know. There just isn't a *clean* way
150
* to break out of 2 nested loops.
153
if (m_apm_compare(tmp1, MM_Two) <= 0)
156
m_apm_subtract(tmp2, tmp1, MM_One);
158
if (iprod1->m_apm_datalength > nd)
162
if (ct == (NDIM - 1))
165
* if the array has filled up, start multiplying
166
* some of the partial products now.
169
m_apm_copy(tmp1, array[mm]);
170
m_apm_multiply(array[mm], iprod1, tmp1);
175
ndigits = ndigits << 1;
184
* store this partial product in the array
185
* and allocate the next array element
188
m_apm_copy(array[ct], iprod1);
189
array[++ct] = m_apm_init();
195
m_apm_copy(array[ct], iprod1);
203
nmul = (kk + 1) >> 1;
207
/* must use tmp var when ii,jj point to same element */
211
m_apm_copy(tmp1, array[ii]);
212
m_apm_multiply(array[jj], tmp1, array[ii+1]);
215
m_apm_multiply(array[jj], array[ii], array[ii+1]);
226
m_apm_copy(array[jj], array[kk]);
232
m_apm_copy(moutput, array[0]);
234
for (ii=0; ii <= ct; ii++)
236
m_apm_free(array[ii]);
244
/****************************************************************************/