Sindbad~EG File Manager

Current Path : /usr/home/beeson/MathXpert/bignums/
Upload File :
Current File : /usr/home/beeson/MathXpert/bignums/binom.c

/* 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