157 lines
4.6 KiB
C
157 lines
4.6 KiB
C
/* mpz_fac_ui(result, n)  Set RESULT to N!.




Copyright (C) 1991 Free Software Foundation, Inc.




This file is part of the GNU MP Library.




The GNU MP Library is free software; you can redistribute it and/or modify


it under the terms of the GNU General Public License as published by


the Free Software Foundation; either version 2, or (at your option)


any later version.




The GNU MP Library is distributed in the hope that it will be useful,


but WITHOUT ANY WARRANTY; without even the implied warranty of


MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the


GNU General Public License for more details.




You should have received a copy of the GNU General Public License


along with the GNU MP Library; see the file COPYING. If not, write to


the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */




#ifdef DBG


#include <stdio.h>


#endif




#include "gmp.h"


#include "gmpimpl.h"


#include "longlong.h"




void


#ifdef __STDC__


mpz_fac_ui (MP_INT *result, unsigned long int n)


#else


mpz_fac_ui (result, n)


MP_INT *result;


unsigned long int n;


#endif


{


#if SIMPLE_FAC




/* Be silly. Just multiply the numbers in ascending order. O(n**2). */




mp_limb k;




mpz_set_ui (result, (mp_limb) 1);




for (k = 2; k <= n; k++)


mpz_mul_ui (result, result, k);


#else




/* Be smarter. Multiply groups of numbers in ascending order until the


product doesn't fit in a limb. Multiply these partial products in a


balanced binary tree fashion, to make the operand have as equal sizes


as possible. (When the operands have about the same size, mpn_mul


becomes faster.) */




mp_limb k;


mp_limb p1, p0, p;




/* Stack of partial products, used to make the computation balanced


(i.e. make the sizes of the multiplication operands equal). The


topmost position of MP_STACK will contain a onelimb partial product,


the second topmost will contain a twolimb partial product, and so


on. MP_STACK[0] will contain a partial product with 2**t limbs.


To compute n! MP_STACK needs to be less than


log(n)**2/log(BITS_PER_MP_LIMB), so 30 is surely enough. */


#define MP_STACK_SIZE 30


MP_INT mp_stack[MP_STACK_SIZE];




/* TOP is an index into MP_STACK, giving the topmost element.


TOP_LIMIT_SO_FAR is the largets value it has taken so far. */


int top, top_limit_so_far;




/* Count of the total number of limbs put on MP_STACK so far. This


variable plays an essential role in making the compututation balanced.


See below. */


unsigned int tree_cnt;




top = top_limit_so_far = 1;


tree_cnt = 0;


p = 1;


for (k = 2; k <= n; k++)


{


/* Multiply the partial product in P with K. */


umul_ppmm (p1, p0, p, k);




/* Did we get overflow into the high limb, i.e. is the partial


product now more than one limb? */


if (p1 != 0)


{


tree_cnt++;




if (tree_cnt % 2 == 0)


{


mp_size i;




/* TREE_CNT is even (i.e. we have generated an even number of


onelimb partial products), which means that we have a


singlelimb product on the top of MP_STACK. */




mpz_mul_ui (&mp_stack[top], &mp_stack[top], p);




/* If TREE_CNT is divisable by 4, 8,..., we have two


similarsized partial products with 2, 4,... limbs at


the topmost two positions of MP_STACK. Multiply them


to form a new partial product with 4, 8,... limbs. */


for (i = 4; (tree_cnt & (i  1)) == 0; i <<= 1)


{


mpz_mul (&mp_stack[top  1],


&mp_stack[top], &mp_stack[top  1]);


top;


}


}


else


{


/* Put the singlelimb partial product in P on the stack.


(The next time we get a singlelimb product, we will


multiply the two together.) */


top++;


if (top > top_limit_so_far)


{


if (top > MP_STACK_SIZE)


abort();


/* The stack is now bigger than ever, initialize the top


element. */


mpz_init_set_ui (&mp_stack[top], p);


top_limit_so_far++;


}


else


mpz_set_ui (&mp_stack[top], p);


}




/* We ignored the last result from umul_ppmm. Put K in P as the


first component of the next singlelimb partial product. */


p = k;


}


else


/* We didn't get overflow in umul_ppmm. Put p0 in P and try


with one more value of K. */


p = p0;


}




/* We have partial products in mp_stack[0..top], in descending order.


We also have a small partial product in p.


Their product is the final result. */


if (top < 0)


mpz_set_ui (result, p);


else


mpz_mul_ui (result, &mp_stack[top], p);


while (top >= 0)


mpz_mul (result, result, &mp_stack[top]);




/* Free the storage allocated for MP_STACK. */


for (top = top_limit_so_far; top >= 0; top)


mpz_clear (&mp_stack[top]);


#endif


}
