A Reasonable Solution to Hamming's Problem


/**************************************************************************
 *
 *          A Nearly Complete Solution to Hamming's Problem.
 *
 *   input:  A list of prime numbers in increasing order entered in the
 *           command tail.
 *
 *  output:  A stream of integers, each having at least one of the given
 *           prime numbers as a factor and only prime numbers from the
 *           given list of primes as factors.  The integers are placed in
 *           the output stream in increasing order.
 * 
 *  limits:  Since the stream is infinite and computer resources are bounded,
 *           not all of the stream can be constructed.  However, the code
 *           below makes no attempt at determining when a resource bound is
 *           reached.
 *
 *
 *                 Conceptual Description of the Code.
 *     
 *    Let ham(p) be the stream of integers representing the solution to the
 *    instance of Hamming's problem given by the list of primes p.  Let
 *    hamming(p,T) be the stream ham(p) multiplied by the positive integer T.
 *    Let car(p) be the first element of the list of primes p.  Let cdr(p) be
 *    the list of primes equal to p with car(p) removed. Let + denote stream
 *    concatenation.  Then
 *
 *       hamming(p,T) = 
 *            if (p == NULL) return(NULL);
 *            else
 *            return(car(p)*T + merge(hamming(p,car(p)*T),hamming(cdr(p),T)))
 *
 *
 *                         Implementation Notes
 *
 *    The recursive solution specified above cannot be implemented directly in
 *    C.  One must first build an abstraction and then compose a solution 
 *    based on that abstraction.  The abstract "machine" which we call queue 
 *    simulates the suspension of the computation of stream tails.  This is
 *    accomplished by storing enough state to uniquely define the future of
 *    the computation of the stream from the point of suspension.  The current
 *    implementation of queue is based on maintaining a linear list and is,
 *    therefore, not optimally efficient.
 *
 **********************************************************************/


#include < stdio.h >

#define ADD 1
#define POP 2

long a[1000][4],head = 1,tail = 0;


/**********************************************************************
 *
 *  queue:   X  -> suspends process & stores state-array
 *           X  -> resume process of least "value"
 *                                   (state-array is output)
 *
 **********************************************************************/


long queue(int func, unsigned long q[])
{
   long tptr, i, val_arg, ptr_arg, t_arg;

   val_arg = q[1];
   ptr_arg = q[2];
   t_arg   = q[3];

   if (func == ADD) 
   {
      tptr = ++tail;  
      while (a[tptr-1][1] > val_arg && tptr > head)
      {
         for (i=1; i<=3; i++) a[tptr][i] = a[tptr-1][i];
         tptr -= 1;
      }
      a[tptr][3] = t_arg;
      a[tptr][2] = ptr_arg;
      a[tptr][1] = val_arg;
   }
   else
   if (func == POP)
   {
      q[1] = a[head][1];
      q[2] = a[head][2];
      q[3] = a[head][3];
      head++; 
   }
}

unsigned long ham(unsigned long p[])
{
   unsigned long right[4], left[4], q[4], i;
   int fbreak();

   right[1]=0; right[2]=0; right[3]=0;
   left[1]=0;  left[2]=0;  left[3]=0;

   q[1] = p[1]; q[2] = 1; q[3] = 1;
   queue(ADD,q);

   while (1)
   { 
      queue(POP,q);
      printf("%ld ",q[1]);  if (getchar()==27) exit(0);

      left[1]=q[3]*p[q[2]+1];     right[1]=q[3]*p[q[2]]*p[q[2]];    
      left[2]=q[2]+1;             right[2]=q[2];
      left[3]=q[3];               right[3]=q[3]*p[q[2]];

      queue(ADD ,right);
      if (left[1]>0) queue(ADD ,left);
   }
}

main(int argc, char **argv)
{
   long p[11],i;

   printf("\nprime list:");
   p[argc]=0;
   for (i=1; i < argc; i++)
   {
      p[i] = atoi(argv[i]);
      printf(" %d",p[i]);
   }
   printf("\n\n");

   ham(p); 
}