Sindbad~EG File Manager
/* compute binomial coefficients of integer or rational arguments */
/* M. Beeson, for Mathpert */
/* Original date 1.15.99 */
/* Last modified 2.21.99 before version 1.624 */
/*
6.17.04 removed 16-bit conditional compilation
4.26.13 removed malloc.h
6.2.13 corrected ratbinomial at line 91
9.22.14 initialized saveheap in ratbinomial
*/
#include <math.h>
#include <string.h> /* memcpy */
#include <assert.h>
#include <stdlib.h>
#include "heap.h" /* callocate and free2 */
#include "bignum.h"
/*____________________________________________________________*/
int intbinomial(long n, long k, bignum *ans)
/* compute (n choose k) and return the result in *ans. It is
assumed that k <= n and both are positive. Return 0 for success.
*/
{ bignum w,d,num,z,r;
long i;
int err;
w = long_to_bignum(1);
for(i=1; i <= k ; i++)
{ d = long_to_bignum(i);
num = long_to_bignum(n-k+i);
/* multiply by num, then divide by d */
bigmult(w,num,&z);
freespace(w.val); /* created by bigint the first time,
else by bigdivide on previous iteration */
err = bigdivide(z,d,&w,&r);
if(err)
return err;
if(r.val[0] != 0 || r.ln != 1)
assert(0); /* it must divide evenly */
freespace(z.val); /* created by bigmult above */
freespace(d.val); /* created by bigint at the top of the loop */
freespace(num.val); /* created by bigint */
}
*ans = w;
return 0;
}
/*___________________________________________________________*/
int ratbinomial(long p, long q, long k, bigrat *ans)
/* compute (p/q choose k), putting it in *ans and returning 0 for success.
It is assumed that k is positive and q is positive but p can be negative.
*/
{ int i;
bigrat r;
bignum bigq,w,v,w2,w3,s,trash,denom,temp,temp2,temp3;
void *saveheap=NULL;
long t;
if(k > 6)
saveheap = heapmax();
if(k == 0)
{ ans->sign = 1;
ans->n = long_to_bignum(1);
ans->d = long_to_bignum(1);
return 0;
}
if(p == 0)
{ ans->sign = 1;
ans->n = long_to_bignum(0);
ans->d = long_to_bignum(1);
return 0;
}
if(k == 1)
{ ans->n = p > 0 ? long_to_bignum(p) : long_to_bignum(-p);
ans->d = long_to_bignum(q);
ans-> sign = p > 0 ? 1 : -1;
return 0;
}
if(q == 1)
{ if(p > 0)
{ if(k > p)
{ ans->sign = 1;
ans->n = long_to_bignum(0);
ans->d = long_to_bignum(1);
return 0;
}
ans->sign = 1;
ans->d = long_to_bignum(1);
return intbinomial(p,k,&ans->n);
}
/* now p < 0 */
ans->sign = (k&1) ? -1 : 1;
ans->d = long_to_bignum(1);
return intbinomial(k-p-1,k,&ans->n);
}
if(q > 0xffff || k > 0x7fff || labs(p) > 0x7fff)
return 38;
/* Now q isn't 1 and k > 1 and p isn't 0 and
both qi and p - qi fit into a long */
r.sign = p > 0 ? 1 : -1;
r.n = p > 0 ? long_to_bignum(p): long_to_bignum(-p);
r.d = long_to_bignum(q);
bigq = long_to_bignum(q);
for(i=1;i<k;i++)
{ /* r = r*(p/q-i)/(i+1) = r *(p-qi)/(q *(i+1)) */
t = p - q*i;
if( t > 0)
bigmult(r.n,long_to_bignum(t),&w);
else
bigmult(r.n,long_to_bignum(-t),&w);
bigmult(bigq,long_to_bignum(i+1),&denom);
biggcd(w,denom,&v);
r.sign *= t > 0 ? 1 : -1;
if(v.ln == 1 && v.val[0] == 1)
{ biggcd(w,r.d,&temp);
if(temp.ln == 1 && temp.val[0] == 1)
{ bigmult(r.d,denom,&s);
r.d = s;
r.n = w;
}
else
{ bigdivide(w,temp,&temp2,&trash);
bigdivide(r.d,temp,&temp3,&trash);
bigmult(temp3,denom,&s);
r.d = s;
r.n = temp2;
}
}
else
{ bigdivide(w,v,&w2,&trash);
if(trash.ln != 1 || trash.val[0] != 0)
assert(0);
bigdivide(denom,v,&w3,&trash);
if(trash.ln != 1 || trash.val[0] != 0)
assert(0);
biggcd(w2,r.d,&temp);
if(temp.ln == 1 && temp.val[0] == 1)
{ bigmult(r.d,w3,&s);
r.n = w2;
r.d = s;
}
else
{ bigdivide(w2,temp,&temp2,&trash);
bigdivide(r.d,temp,&temp3,&trash);
bigmult(temp3,w3,&s);
r.n = temp2;
r.d = s;
}
}
}
*ans = r;
if(k > 6)
{ /* stash r somewhere temporarily and reset_heap */
digit *temp1 = (digit *) calloc(r.n.ln, sizeof(digit));
digit *temp2 = (digit *) calloc(r.d.ln, sizeof(digit));
if(!temp1 || !temp2)
return 0; /* never mind memory management */
memcpy(temp1,r.n.val,r.n.ln *sizeof(digit));
memcpy(temp2,r.d.val,r.d.ln *sizeof(digit));
reset_heap(saveheap);
r.n.val = getspace(r.n.ln);
r.d.val = getspace(r.d.ln);
memcpy(r.n.val, temp1, r.n.ln *sizeof(digit));
memcpy(r.d.val, temp2, r.d.ln *sizeof(digit));
free(temp1);
free(temp2);
}
return 0;
}
/*__________________________________________________________________*/
int bignum_long(bignum b, long *ans)
/* convert bignum to long if possible, returning 0 for success, 2 for failure */
/* don't make any assumptions about word size, sizeof(long), sizeof(digit),
except that sizeof(digit) is either half of sizeof(long) or equal to sizeof(long).
*/
{ if(b.ln > 2)
return 2; /* impossible conversion */
if(b.ln == 1 && sizeof(long) > sizeof(digit) )
{ *ans = (long) b.val[0];
return 0;
}
if(b.ln == 1 && !(b.val[0] >> (NN-1)))
{ *ans = (long) b.val[0];
return 0;
}
if(b.ln == 1)
return 2;
return 2;
}
Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists