/* L-24 MCS 572 Friday 10 March 2006 : knapsack problem */

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int write_choice ( int n, double weight[n], int choice[n],
                   double sum, int cnt );

/* DESCRIPTION :
 *   Writes the choice of weights and asked the user to continue or not. 
 *
 * ON ENTRY :
 *   n          number of items to consider;
 *   weight     weight of every item;
 *   choice     if item i is chosen, then choice[i] equals 1,
 *              otherwise choice[i] equals 0;
 *   sum        total weight of the selected items;
 *   cnt        number of items chosen.
 *
 * ON RETURN :
 *   if 0, then the user will not continue the search,
 *   if 1, then the user wants the search to continue. */

int count_choice ( int n, double weight[n], int choice[n],
                   double sum, int cnt );
/*
 * DESCRIPTION :
 *   Counts the number of times this function is called, returning 1
 *   each time.  Returns the number of counts when n < 0 on input. */

int pack ( int n, int k, double weight[n], int choice[n], double sum,
           double min_sum, double max_sum, int cnt, int max_items,
           int (*process) ( int m, double *w, int *c, double s, int nb ) );

/* DESCRIPTION :
 *   Exhaustive search to select items to pack a knapsack.
 *
 * ON ENTRY :
 *   n          total number of items to consider;
 *   k          start enumeration at item k;
 *   weight     weight of every item;
 *   choice     current choice: choice[i] == 1 if i is chosen;
 *   sum        sum of weights of the current choice;
 *   min_sum    minimal sum of weight to meet requirement;
 *   max_sum    sum may not exceed this value;
 *   cnt        current number of items selected;
 *   max_item   cnt may not exceed this value;
 *   process    is called whenever a new good sum has been found.
 *
 * ON RETURN :
 *   1 if search must continue, 0 if search must stop. */

int main ( int argc, char *argv[] )
{
   int n,i,max,*c;
   double *w,min_sum,max_sum;
   int interactive = 0;

   srand(time(NULL));

   printf("Give number of items : "); scanf("%d",&n);

   w = (double*)calloc(n,sizeof(double));
   c = (int*)calloc(n,sizeof(int));
   for(i=0; i<n; i++)
   {
      w[i] = ((double) rand())/RAND_MAX;
      c[i] = 0;
   }

   printf("The weights :\n");
   for(i=0; i<n; i++) printf("%.15lf\n",w[i]);

   printf("Give the minimal sum of weights : "); scanf("%lf",&min_sum);
   printf("Give the maximal sum of weights : "); scanf("%lf",&max_sum);
   printf("Give the maximal number of items : "); scanf("%d",&max);

   if(interactive==1)
      pack(n,0,w,c,0.0,min_sum,max_sum,0,max,write_choice);
   else
   {
      pack(n,0,w,c,0.0,min_sum,max_sum,0,max,count_choice);
      printf("Counted %d valid choices.\n",count_choice(-1,w,c,0,0));
   }
   return 0;
}

int pack ( int n, int k, double weight[n], int choice[n], double sum,
           double min_sum, double max_sum, int cnt, int max_items,
           int (*process) ( int m, double *w, int *c, double s, int nb ) )
{
   if((k < n) && (sum <= max_sum) && (cnt < max_items))
   {
      int i;

      for(i=k; i<n; i++)
      {
         if(sum + weight[i] <= max_sum)      /* we pick the item */
         {
            choice[i] = 1;                   /* add item to the bag */

            if(sum + weight[i] >= min_sum)   /* found good choice */
            {
               if(process(n,weight,choice,sum+weight[i],cnt+1) == 0)
                  return 0;                  /* interrupt the search */
            }
	    else
            {
               if(pack(n,i+1,weight,choice,sum+weight[i],min_sum,max_sum,
                       cnt+1,max_items,process) == 0)
                  return 0;                  /* interrupt the search */
            }
            choice[i] = 0;                   /* skip the item */
         }
      }
   }
   return 1;                                 /* continue the search */
}

int write_choice ( int n, double weight[n], int choice[n],
                   double sum, int cnt )
{
   int i;

   printf("Selected %d items with total weight %.15lf :\n",cnt,sum);
   for(i=0; i<n; i++)
      if(choice[i] == 1)
         printf("item %d weighs %.15lf\n",i,weight[i]);

   printf("Do you want to continue ? (1 = yes, 0 = no) ");
   scanf("%d",&i);

   return i;
}

int count_choice ( int n, double weight[n], int choice[n],
                   double sum, int cnt )
{
   static int counter = 0;

   if(n < 0) 
      return counter;
   else
   {
      counter++;
      return 1;
   }
}
