Return Styles: Pseud0ch, Terminal, Valhalla, NES, Geocities, Blue Moon. Entire thread

1000 factorial

Name: Anonymous 2011-04-02 22:59

Well, someone wrote that they could write a program to do 1000! in one line of Haskell. I said that it would take less than 100 lines of C to do the same. I wrote the program, and it is less than 100 lines of C:

#include <stdio.h>
#include <string.h>

#define LENGTH 10000

int max (int a, int b) { return (a > b) ? a : b; }
int min (int a, int b) { return (b > a) ? a : b; }

int length (unsigned char * a)
 {
   int i;
   i = LENGTH - 1;
   while ((a[i] == 0) && (i >= 0)) i--;
   return i + 1;
 }

void add (unsigned char * a, unsigned char * b)
 {
   int i, c, C;
   c = min(max(length(a), length(b)) + 1, LENGTH);
   for (i = 0, C = 0; i < c; i++)
    {
      a[i] += b[i] + C;
      C = a[i] > 99;
      if (C) a[i] -= 100;
    }
 }

   unsigned char x [LENGTH], y [LENGTH];
void mul (unsigned char * a, unsigned char * b)
 {
   int i, j, c, d, C;
   memset(y, '\0', LENGTH);
   c = min(length(a) + length(b) + 1, LENGTH);
   d = length(b);
   for (i = 0; i < d; i++)
    {
      memset(x, '\0', LENGTH);
      for (j = 0, C = 0; (i + j) < c; j++)
       {
         x[i + j] = (a[j] * b[i] + C) % 100;
         C = (a[j] * b[i] + C) / 100;
       }
      add(y, x);
    }
   memcpy(a, y, LENGTH);
 }

void numset (unsigned char * a, unsigned long b)
 {
   int i;
   for (i = 0; i < LENGTH; i++)
    {
      a[i] = b % 100;
      b /= 100;
    }
 }

int tz (unsigned char * a)
 {
   int i;
   for (i = 0; i < LENGTH; i++) if (a[i] != 0) return 0;
   return 1;
 }

   unsigned char minus1 [LENGTH], prod [LENGTH];
void fact (unsigned char * a)
 {
   numset(prod, 1);
   while (!tz(a))
    {
      mul(prod, a);
      add(a, minus1);
    }
   memcpy(a, prod, LENGTH);
 }

   unsigned char arg_res [LENGTH];
int main (void)
 {
   unsigned long a;
   int i;
   memset (minus1, 99, LENGTH);
   printf("Input the number to take the factorial of: ");
   scanf("%lu", &a);
   numset(arg_res, a);
   fact(arg_res);
   i = LENGTH - 1;
   while (arg_res[i] == 0) i--;
   while (i >= 0)
    {
      printf("%02d", (int) arg_res[i]);
      i--;
    }
   putchar('\n');
   return 0;
 }

Name: Anonymous 2011-04-03 23:49

>>25
>>21
I'm not the deluded pythonista! I'm usually respectful, and knew that >>13 is not a valid entry.
I tried to do it in C, and wanted to make the bignum part faster, smaller (using a whole uintmax) and general, but it seems I can't write C code anymore. I give up, it would be much easier in Assembly.

What I've done so far:

#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <string.h>

#define SIZE(sz) (sizeof(uintmax_t)*(sz))

typedef struct {
     uintmax_t *val;
     size_t len;
} bignum;

static inline max (size_t a, size_t b) {return a>b?a:b;}
static inline min (size_t a, size_t b) {return a<b?a:b;}

bignum *bignum_alloc(size_t sz) {
     bignum *n = malloc(sizeof(bignum));
     n->len = sz;
     n->val = malloc(SIZE(sz));
     return n;
}

bignum *bignum_copy(bignum *n) {
     bignum *m = bignum_alloc(n->len);
     memcpy(m->val, n->val, n->len);
     return m;
}

void bignum_realloc(bignum *n, size_t sz, uintmax_t val) {
     size_t oldsz = n->len;
     n->val = realloc(n->val, SIZE(sz));
     n->len = sz;
     n->val[oldsz] = val;
     if (oldsz < sz-1)
      memset(n+oldsz, 0, oldsz+1);
}

void bignum_dealloc(bignum *n) {
     free(n->val);
     free(n);
}

bignum *bignum_from_uintmax(uintmax_t n) {
     bignum *m = bignum_alloc(1);
     m->val[0] = n;
     return m;
}

void bignum_addbi(bignum *n, uintmax_t m) {
     size_t i, j = n->len;
     uintmax_t a;

     for (a = n->val[i], i = 0; m && i < j; i++) {
      a = n->val[i];
      n->val[i] += m;
      m = a > n->val[i];
     }
     if (m && i >= n->len)
      bignum_realloc(n, n->len+1, 1);
}


void bignum_addbb(bignum *n, bignum *m) {
     size_t i, j = max(n->len, m->len);
     uintmax_t a; int c;
     if (n->len < m->len)
      bignum_realloc(n, m->len, 0);
     for (i = 0, c = 0; i < j; i++) {
      a = n->val[i];
      n->val[i] += m->val[i] + c;
      c = a > n->val[i];
     }
     if (c && i >= j++)
      bignum_realloc(n, j, 1);
}

void bignum_subbi(bignum *n, uintmax_t m) {
     size_t i, j = n->len;
     uintmax_t a;

     for (a = n->val[i], i = 0; m && i < j; i++) {
      a = n->val[i];
      n->val[i] -= m;
      m = a < n->val[i];
     }
     if (--j && !n->val[j])
      bignum_realloc(n, j, n->val[j]);
}

void bignum_mulbb(bignum *n, bignum *m) {
     if (bignum_zerop(m)) {
      bignum_dealloc(n);
      n = bignum_from_uintmax(0);
     }
     if (bignum_equalpi(m, 1)) return;
     bignum *i = bignum_copy(m);
     bignum *l = bignum_copy(n);
     for (; !bignum_equalpi(i, 1); bignum_subbi(i, 1))
      bignum_addbb(n, l);
     bignum_dealloc(i);
     bignum_dealloc(l);
}

void bignum_mulbi(bignum *n, uintmax_t m) {
     if (!m) {
      bignum_dealloc(n);
      n = bignum_from_uintmax(0);
     }
     if (m == 1) return;
     bignum *l = bignum_copy(n);
     while (--m)
      bignum_addbb(n, l);
}

int bignum_zerop(bignum *n) {
     return (!(!(n->len-1) && n->val[0]));
}

int bignum_equalp(bignum *n, bignum *m) {
     if (n->len != m->len) return 0;
     size_t i = n->len;
     while (n->val[i] == m->val[i]) i--;
     return !!i;
}

inline int bignum_equalpi(bignum *n, uintmax_t m) {
     return (!(n->len > 1)) && (n->val[0] == m);
}

void debug_print_bn(bignum *n) {
     printf("Memory Location: %#08x; %#08x\n", n, n->val);
     printf("Length: %u\n", n->len);
     size_t i;
     for (i = 0; i < n->len; i++)
      printf("Cell#%08u: %016llx\n", i, n->val[i]);
}

Newer Posts
Don't change these.
Name: Email:
Entire Thread Thread List