/* * factorial2 - implementation of different factorial related functions * * Copyright (C) 2013 Christoph Zurnieden * * Calc is open software; you can redistribute it and/or modify it under * the terms of the version 2.1 of the GNU Lesser General Public License * as published by the Free Software Foundation. * * Calc 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 Lesser General * Public License for more details. * * A copy of version 2.1 of the GNU Lesser General Public License is * distributed with calc under the filename COPYING-LGPL. You should have * received a copy with calc; if not, write to Free Software Foundation, Inc. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * @(#) $Revision: 30.4 $ * @(#) $Id: factorial2.cal,v 30.4 2013/08/18 20:01:53 chongo Exp $ * @(#) $Source: /usr/local/src/bin/calc/cal/RCS/factorial2.cal,v $ * * Under source code control: 2013/08/11 01:31:28 * File existed as early as: 2013 */ /* * hide internal function from resource debugging */ static resource_debug_level; resource_debug_level = config("resource_debug", 0); /* get dependencies */ read -once factorial toomcook specialfunctions; /* Factorize a factorial and put the result in a 2-column matrix with pi(n) rows mat[ primes , exponent ] Result can be restricted to start at a prime different from 2 with the second argument "start". That arguments gets taken at face value if it prime and smaller than n, otherwise the next larger prime is taken if that prime is smaller than n. */ define __CZ__factor_factorial(n,start){ local prime prime_list k pix stop; if(!isint(n)) return newerror("__CZ__factor_factorial(n,start): n is not integer"); if(n < 0) return newerror("__CZ__factor_factorial(n,start): n < 0"); if(n == 1) return newerror("__CZ__factor_factorial(n,start): n == 1"); if(start){ if(!isint(start) && start < 0 && start > n) return newerror("__CZ__factor_factorial(n,start): value of " "parameter 'start' out of range"); if(start == n && isprime(n)){ prime_list = mat[1 , 2]; prime_list[0,0] = n; prime_list[0,1] = 1; } else if(!isprime(start) && nextprime(start) >n) return newerror("__CZ__factor_factorial(n,start): value of parameter " "'start' out of range"); else{ if(!isprime(start)) prime = nextprime(start); else prime = start; } } else prime = 2; pix = pix(n); if(start){ pix -= pix(prime) -1; } prime_list = mat[pix , 2]; k = 0; do { prime_list[k ,0] = prime; prime_list[k++,1] = __CZ__prime_divisors(n,prime); prime = nextprime(prime); }while(prime <= n); return prime_list; } /* subtracts exponents of n_1! from exponents of n_2! with n_1<=n_2 Does not check for size or consecutiveness of the primes or a carry */ define __CZ__subtract_factored_factorials(matrix_2n,matrix_n){ local k ret len1,len2,tmp count p e; len1 = size(matrix_n)/2; len2 = size(matrix_2n)/2; if(len2 0){ shift = matrix[0,1]; if(pix-1 == 0) return 2^matrix[0,1]; } /* This is a more general way to do the multiplication, so any optimization must have been done by the caller. */ k = 0; /* The size of the largest exponent in bits is calculated dynamically. Can be done more elegantly and saves one run over the whole array if done inside the main loop. */ hb =0; for(k=0;k=0;k1--){ /* the cut-off for T-C-4 ist still too low, using T-C-3 here TODO: check cutoffs */ result = toomcook3square(result); for(k=start; k<=k2; k++) { if((matrix[k,1] & (1 << k1)) != 0) { result *= matrix[k,0]; } } } result <<= shift; return result; } /* Compute binomial coeficients n!/(k!(n-k)!) One of the rare cases where a formula once meant to ease manual computation is actually the (aymptotically) fastest way to do it (in July 2013) for the extreme case binomial(2N,N) but for a high price, the memory needed is pi(N)--theoretically. */ define binomial(n,k){ local ret factored_n factored_k factored_nk denom num quot K prime_list prime; local pix diff; if(!isint(n) || !isint(k)) return newerror("binomial(n,k): input is not integer"); if(n<0 || k<0) return newerror("binomial(n,k): input is not >= 0"); ; if(n5e4) Needs a lot of memory. */ define bigcatalan(n){ if(!isint(n) )return newerror("bigcatalan(n): n is not integer"); if( n<0) return newerror("bigcatalan(n): n < 0"); if( n<5e4 && !isdefined("test8900") ) return catalan(n); return binomial(2*n,n)/(n+1); } /* df(-111) = -1/3472059605858239446587523014902616804783337112829102414124928 7753332469144201839599609375 df(-3+1i) = 0.12532538977287649201-0.0502372106177184607i df(2n + 1) = (2*n)!/(n!*2^n) */ define __CZ__double_factorial(n){ local n1 n2 diff prime pix K prime_list k; prime = 3; pix = pix(2*n)+1; prime_list = mat[pix , 2]; K = 0; do { prime_list[K ,0] = prime; diff = __CZ__prime_divisors(2*n,prime)-( __CZ__prime_divisors(n,prime)); if(diff != 0) prime_list[K++,1] = diff; prime = nextprime(prime); }while(prime <= n); do { prime_list[K ,0] = prime; prime_list[K++,1] = __CZ__prime_divisors(2*n,prime); prime = nextprime(prime); }while(prime <= 2*n); return __CZ__multiply_factored_factorial(prime_list,K); /* n1=__CZ__factor_factorial(2*n,1); n1[0,1] = n1[0,1]-n; n2=__CZ__factor_factorial(n,1); diff=__CZ__subtract_factored_factorials( n1 , n2 ); return __CZ__multiply_factored_factorial(diff); */ } ##1, 1, 3, 15, 105, 945, 10395, 135135, 2027025, 34459425, 654729075, ##13749310575, 316234143225, 7905853580625, 213458046676875, ##6190283353629375, 191898783962510625, 6332659870762850625, ##221643095476699771875, 8200794532637891559375 ## 1, 2, 8, 48, 384, 3840, 46080, 645120, 10321920, 185794560, ##3715891200, 81749606400, 1961990553600, 51011754393600, ##1428329123020800, 42849873690624000, 1371195958099968000, ##46620662575398912000, 1678343852714360832000, 63777066403145711616000 define doublefactorial(n){ local n1 n2 diff eps ret; if(!isint(n) ){ /* Probably one of the not-so-good ideas. See result of http://www.wolframalpha.com/input/?i=doublefactorial%28a%2Bbi%29 */ eps=epsilon(epsilon()*1e-2); ret = 2^(n/2-1/4 * cos(pi()* n)+1/4) * pi()^(1/4 * cos(pi()* n)-1/4)* gamma(n/2+1); epsilon(eps); return ret; } if(n==2) return 2; if(n==3) return 3; switch(n){ case -1: case 0 : return 1;break; case 2 : return 2;break; case 3 : return 3;break; case 4 : return 8;break; default: break; } if(isodd(n)){ /* TODO: find reasonable cutoff df(2n + 1) = (2*n)!/(n!*2^n) */ if(n>0){ n = (n+1)//2; return __CZ__double_factorial(n); } else{ if(n == -3 ) return -1; n = ((-n)-1)/2; return ((-1)^-n)/__CZ__double_factorial(n); } } else{ /* I'm undecided here. The formula for complex n is valid for the negative integers, too. */ n = n>>1; if(n>0){ if(!isdefined("test8900")) return factorial(n)<= n && __CZ__stirling1_m >= m){ return __CZ__stirling1[n,m]; } else{ __CZ__stirling1 = mat[n+1,m+1]; __CZ__stirling1[0,0] = 1; for(i=1;i<=n;i++) __CZ__stirling1[i,0] = 0; for(i=1;i<=n;i++){ for(j=1;j<=m;j++){ if(j<=i){ __CZ__stirling1[i, j] = __CZ__stirling1[i - 1, j - 1] - (i - 1)\ * __CZ__stirling1[i - 1, j]; } else{ __CZ__stirling1[i, j] = 0; } } } __CZ__stirling1_n = n; __CZ__stirling1_m = m; return __CZ__stirling1[n,m]; } } define stirling2(n,m){ local k sum; if(n<0)return newerror("stirling2(n,m): n < 0"); if(m<0)return newerror("stirling2(n,m): m < 0"); if(n= n && __CZ__stirling2_m >= m){ return __CZ__stirling2[n,m]; } else{ __CZ__stirling2 = mat[n+1,m+1]; __CZ__stirling2[0,0] = 1; for(i=1;i<=n;i++){ __CZ__stirling2[i,0] = 0; for(j=1;j<=m;j++){ if(j<=i){ __CZ__stirling2[i, j] = __CZ__stirling2[i -1, j -1] + (j )\ * __CZ__stirling2[i - 1, j]; } else{ __CZ__stirling2[i, j] = 0; } } } } __CZ__stirling2_n = (n); __CZ__stirling2_m = (m); return __CZ__stirling2[n,m]; } define bell(n){ local sum s2list k A; if(!isint(n)) return newerror("bell(n): n is not integer"); if(n < 0) return newerror("bell(n): n is not positive"); /* place some more shortcuts here?*/ if(n<=15){ mat A[16] = { 1, 1, 2, 5, 15, 52, 203, 877, 4140, 21147, 115975, 678570, 4213597, 27644437, 190899322, 1382958545 }; return A[n]; } /* Start by generating the list of stirling numbers of the second kind */ s2list = stirling2caching(n,n//2); if(iserror(s2list)) return newerror("bell(n): could not build stirling num. list"); sum = 0; for(k=1;k<=n;k++){ sum += stirling2caching(n,k); } return sum; } define subfactorialrecursive(n){ if(n==0) return 1; if(n==1) return 0; if(n==2) return 1; return n * subfactorialrecursive(n-1) + (-1)^n; } /* This is, quite amusingely, faster than the very same algorithm in PARI/GP + GMP*/ define subfactorialiterative(n){ local k temp1 temp2 ret; if(n==0) return 1; if(n==1) return 0; if(n==2) return 1; temp1 = 0; ret = 1; for(k=3;k<=n;k++){ temp2 = temp1; temp1 = ret; ret = (k-1) *(temp1 + temp2); } return ret; } define subfactorial(n){ local epsilon eps ret lnfact; if(!isint(n))return newerror("subfactorial(n): n is not integer."); if(n < 0)return newerror("subfactorial(n): n < 0"); return subfactorialiterative(n); } define risingfactorial(x,n){ local num denom quot ret; if(n == 1) return x; if(x==0) return newerror("risingfactorial(x,n): x == 0"); if(!isint(x) || !isint(n)){ return gamma(x+n)/gamma(x); } if(x<1)return newerror("risingfactorial(x,n): integer x and x < 1"); if(x+n < 1)return newerror("risingfactorial(x,n): integer x+n and x+n < 1"); if(x<9000&&n<9000){ return (x+n-1)!/(x-1)!; } else{ num = __CZ__factor_factorial(x+n-1,1); denom = __CZ__factor_factorial(x-1,1); quot = __CZ__subtract_factored_factorials( num , denom ); free(num,denom); ret = __CZ__multiply_factored_factorial(quot); return ret; } } define fallingfactorial(x,n){ local num denom quot ret; if(n == 0) return 1; if(!isint(x) || !isint(n)){ if(x == n) return gamma(x+1); return gamma(x+1)/gamma(x-n+1); } else{ if(x<0 || x-n < 0) return newerror("fallingfactorial(x,n): integer x<0 or x-n < 0"); if(x == n) return factorial(x); if(x<9000&&n<9000){ return (x)!/(x-n)!; } else{ num = __CZ__factor_factorial(x,1); denom = __CZ__factor_factorial(x-n,1); quot = __CZ__subtract_factored_factorials( num , denom ); free(num,denom); ret = __CZ__multiply_factored_factorial(quot); return ret; } } } /* * restore internal function from resource debugging * report important interface functions */ config("resource_debug", resource_debug_level),; if (config("resource_debug") & 3) { print "binomial(n,k)"; print "bigcatalan(n)"; print "doublefactorial(n)"; print "subfactorial(n)"; print "stirling1(n,m)"; print "stirling2(n,m)"; print "stirling2caching(n,m)"; print "bell(n)"; print "subfactorial(n)"; print "risingfactorial(x,n)"; print "fallingfactorial(x,n)"; }