Fuzzy Logic in C: An Update

John A.R. Tucker, Phillip E. Fraley, and Lawrence P. Swanson

John teaches computer courses at Albright College and Reading Area Community College; Phillip is working on several projects, including proton models, large color images, and neural networks; Lawrence currently works as a test engineer. They can be reached through the DDJoffices.


Early last year, we were looking for a software implementation of fuzzy logic. Greg Viot's article "Fuzzy Logic in C" (DDJ, February 1993) was a step towards what we needed, but it didn't include the necessary initialization, parsing, and output functions. Consequently, we filled in the gaps by writing functions that, together with Greg's code, make a working fuzzy-logic program you can use. Listing One (page 101) is the complete source code for the updated version (which includes Greg's original code and our additions). The enhancements, which we'll focus on in this article, are shaded as well as identified in the comments. For background on fuzzy logic in general and Greg's techniques in particular, refer to his original article.

Rule Files and Structures

We saw right away that the parsing of the rules file would be a problem to generalize for all possible combinations of antecedents and consequences, so we elected to simplify the problem by allowing only two antecedents and one consequence.

The generalized case would have resulted in loss of clarity. We didn't try to be clever about our functions; in fact, they are quite direct (three segments are repetitive). The extensive use of linked lists and pointers to structures in initialize_system() related to the rules are, however, quite involved. Nor did we optimize or generalize the code. This makes it possible for you to modify the code to accept other input files by copying existing code segments and making minor adjustments. Finally, we took full advantage of understanding the input data structures for the specific example of the inverted-pendulum problem Greg described.

To allow for easy alteration of the fuzzy sets or rule definitions, we used three ASCII files with fixed names and formats as the input files that describe the fuzzy sets (angle, velocity, and force). Similarly, an ASCII file is used to describe the rules file. These four files are to be located in a common directory from which the program is run.

In the three files describing the fuzzy sets (in1, in2, and out1), you can use any name ten characters or less in length on the first line as a name for the input fuzzy set. The first column of the subsequent lines is for the name of the membership element of that fuzzy set, again limited to ten characters. The next four columns describe the corner points of the membership (if the third and fourth columns are the same, the shape is a triangle). White-space, spaces, or tabs separate the columns. You may have as many rows of membership elements as you please, but five, seven, or nine seem to be the best choices. Take care not to include any blank rows.

The first file, in1 (angle), looks like Figure 1. The files in2 (velocity) and out1 (force) are similar. In initialize_

system(), we have three nearly identical code fragments. You can block copy them and make the few changes required. The cycle is as follows: Open the file, set a pointer and allocate memory, read the fuzzy set's name, read a line of data from the file, set a pointer to the next structure, assign values to the structure elements, and lastly, close the file. The differences in these three segments are in lines 1 and 2 (the filenames are different), lines 5 and 6 (the pointers point to differing places), and lines 27 and 33, where the filenames in the error messages are different.

We included an error trap to detect if either slope1 or slope2 is less than or equal to 0, a condition not allowed in the original program. If such an error is encountered, the program exits with appropriate information. The setting of the pointers for the rules file is more complex. In the original article, Greg suggested a file that looked like Figure 2(a). Although we liked the form of this file, it was complex to parse so we stripped the file to its essential elements: the name of the fuzzy set elements and the order in which they appear in each rule; see Figure 2(b). We used an awk and sed pipeline to strip the "rule" file and create a more suitable form for parsing in a file named "rules." (The command line awk '{print $6, $10, $14}' rule|sed 's/)//g'> rules does this elegantly. You can create the rules file directly, as with the input files, and eliminate the clearer representation of the rule file entirely if you do not have these tools.) You can have more or less than the 15 rules in Greg's article. Add or delete them as you please, one rule per row. Be certain that membership-element names are exact matches in all the files, including the rules file. In particular, note that upper and lower case are not equivalent.

Once initialize_system() is written, you're limited to two inputs and one output. You will need to make changes to the arguments for fscanf() and define new buffers to accommodate any other combination.

The insight to the rules structures initialization is that structures of rule_

type and rule_element_type form the acceptable rules at the time of initialization. That is, appropriate fuzzy inputs (antecedents) are associated (linked) with a fuzzy output (consequence) as defined in the rules at the time the rules file is read. Values in the mf_type structure are pointed to by the pointer stored in the rule_element_type *value. Later, if a 0 is pointed to by any of the if_side value pointers, the function defuzzification() will equate to 0, and subsequent calculation of the sum_of_products and sum_of_areas will not be affected. See Figure 3 for the complete relationship of all the data structures and their pointers.

Using the Updated Program

To illustrate how you can use the updated fuzzy-logic program, we'll refer you to the rule in Figure 4, where we begin by opening the rules file, allocating memory for a structure rule_type and setting a pointer to it, and scanning the rules one line at a time. As each line (rule) is read, we "know" that the first field in the line is the angle (structure io_type, pointed to by *membership_functions), so we begin searching its fuzzy-set members (structures mf_type), doing a string match on the membership element name, NL. When the match is found, memory for a rule_element_type structure is allocated, the address to the value element of the matching mf_structure is stored, and a pointer to rule_type, pointed to by the *if_side. A pointer to the second field (in this case, velocity) is also established as a pointer (element *next) in rule_element_type.

The second field of the rule (velocity) is then used to search for a string match on its membership-element name, ZE, in the second io_structure *membership_function, and a pointer to the address where its value is located is stored in the next rule_element_type *value. Finally, the last element of the rule, the consequence (force, in our example), is treated in the same manner: The address of the match is stored in the rule_

element_type *value pointed to by the rule_type *then side when the appropriate membership element name, PL, is matched.

These steps are repeated for every rule in the rules file; refer again to the first three rules in Figure 3.

To complete the alterations, other changes included placing the two anchor pointers System_Output and System_Inputs as global pointers along with the existing Rule_Base, adding macro definitions for max and min for cross-compiling onto MS-DOS platforms, and adding the #include for the function strcmp(); see Example 1. We also included the necessary function to accept two inputs from the command line as arguments for the initial condition get_system_inputs() and a function put_system_outputs() to examine the exit status of a single inference pass on the input data.

After using the code with various inputs, we needed to add error traps because we were getting core dumps with certain input. These were caused by division by zero when there were no rules in the set to cover the condition. Consequently, we added the code in Example 2(a) to the original function defuzzification(). We also added Example 2(b) to rule_evaluation().

To further illustrate how you can use the program, assume the scaled angle of 60 and a scaled velocity of 125 as in Figure 5. The line force: Value=134 reflects the defuzzified and scaled single-valued output for the two inputs. It would be instructive to interface this program to a graphics output device where a loop could be created and the inverted pendulum balanced. Alternatively, a batch file or shell script could feed new inputs and use the output to generate the two new inputs, storing intermediate data in a file. Or, you might try graphing the trapezoidal output areas made on each iteration.

Completing a

fuzzy-based

inference engine

Figure 1: The in1 file; values can be altered as desired.

              Angle
NL       0      31      31      63
NM      31      63      63      95
NS      63      95      95     127
ZE      95     127     127     159
PS     127     159     159     191
PM     159     191     191     223
PL     191     223     223     255

Figure 2: (a) Original rules file; (b) modified rules file.

(a)
    rule 1: IF (angle is NL) AND (velocity is ZE) THEN (force is PL)
    rule 2: IF (angle is ZE) AND (velocity is NL) THEN  (force is PL)
    rule 3: IF (angle is NM) AND (velocity is ZE) THEN (force is PM)
     .
     .
     .

    rule 15: IF (angle is PL) AND (velocity is ZE) THEN (force is NL)

(b)
    NL ZE PL
    ZE NL PL
    NM ZE PM
       .
       .
       .
    PL ZE NL

Figure 3: Relationship of data structures and their pointers.

Figure 4: Sample rule used to develop Figure 3.

rule 1: IF (angle is NL) AND (velocity is ZE)
THEN (force is PL)

Figure 5: Output generated with a scaled angle of 60 and scaled velocity of 125.

fuzz 60 125
angle: Value=60
  NL: Value 21 Left 0 Right 63
  NM: Value 203 Left 31 Right 95
  NS: Value 0 Left 63 Right 127
  ZE: Value 0 Left 95 Right 159
  PS: Value 0 Left 127 Right 191
  PM: Value 0 Left 159 Right 223
  PL: Value 0 Left 191 Right 255
velocity: Value=125
  NL: Value 0 Left 0 Right 64
  NM: Value 0 Left 31 Right 95
  NS: Value 14 Left 63 Right 127
  ZE: Value 210 Left 95 Right 159
  PS: Value 0 Left 127 Right 191
  PM: Value 0 Left 159 Right 223
  PL: Value 0 Left 191 Right 255
force: Value=134
  NL: Value 0 Left 0 Right 63
  NM: Value 203 Left 31 Right 95
  NS: Value 0 Left 63 Right 127
  ZE: Value 0 Left 95 Right 159
  PS: Value 0 Left 127 Right 191
  PM: Value 203 Left 159  Right 223
  PL: Value 21 Left 191 Right 255
  Rule #1: 21 210 21
  Rule #2: 0 0 21
  Rule #3: 203 210 203
  Rule #4: 0 0 203
  Rule #5: 0 210 0
  Rule #6: 0 14 0
  Rule #7: 0 0 0
  Rule #8: 0 210 0
  Rule #9: 0 0 0
  Rule #10: 0 210 0
  Rule #11: 0 14 0
  Rule #12: 0 0 203
  Rule #13: 203 210 203
  Rule #14: 0 0 0
  Rule #15: 0 210 0


Example 1: Adding the #include, global pointers, and macros.

#include     <string.h>
#define max(a,b) (a<b ? b : a)
#define min(a,b) (a>b ? b : a)
struct io_type *System_Inputs;
struct io_type *System_Output;

Example 2: (a) Code added to the original function defuzzification(); (b) code added to rule_evaluation().

(a)
if(sum_of_areas==0)

{ printf("Sum of Areas = 0, will cause div error\n");

printf("Sum of Products= %d\n",sum_of_products);

so->value=0;

return;

} (b) int nomatch=0;

for(tp=rule->then_side;tp!=NULL;tp=tp->next)

{ *(tp->value)=max(strength,*(tp->value));

if(strength>0)nomatch=1;

}

}

if(nomatch==0)printf("NO MATCHING RULES FOUND!\n");

[LISTING ONE]


/* Update to Greg Viot's fuzzy system -- DDJ, February 1993, page 94 */
/* By J. Tucker, P. Fraley, and L. Swanson, April 1993 */
#include <stdio.h>
#include <string.h>                      /* NEW */
# define max(a,b)   (a<b ? b : a)        /* NEW */
# define min(a,b)   (a>b ? b : a)        /* NEW */
struct io_type *System_Inputs;           /* anchor inputs NEW */
struct io_type *System_Output;           /* anchor output NEW */
#define MAXNAME 10
#define UPPER_LIMIT 255
struct io_type{
   char name[MAXNAME];
   int value;
   struct mf_type *membership_functions;
   struct io_type *next;
   };
struct mf_type{
   char name[MAXNAME];
   int value;
   int point1;
   int point2;
   float slope1;
   float slope2;
   struct mf_type *next;
   };
struct rule_type{
   struct rule_element_type *if_side;
   struct rule_element_type *then_side;
   struct rule_type *next;
   };
struct rule_element_type{
   int *value;
   struct rule_element_type *next;
   };
struct rule_type *Rule_Base;

main(argc,argv)                          /* NEW */
int argc;                                /* NEW */
char *argv[];                            /* NEW */
{  int input1, input2;                   /* NEW */
   if(argc!=3)                           /* NEW */
   {  printf("Error - Must supply 2 numeric inputs.\n"); /* NEW */
      printf("        Inputs scaled to range 0-255.\n"); /* NEW */
      printf("Usage: %s angle velocity\n",argv[0]);      /* NEW */
      exit(0);                           /* NEW */
   }                                     /* NEW */
   input1=atoi(argv[1]);                 /* NEW */
   input2=atoi(argv[2]);                 /* NEW */
   initialize_system();                  /* Read input files, NEW */
   get_system_inputs(input1,input2);     /* Get & put argv NEW */
   fuzzification();
   rule_evaluation();
   defuzzification();
   put_system_outputs();                 /* print all data, NEW */
}                                        /* END MAIN */
fuzzification()
{  struct io_type *si;
   struct mf_type *mf;
   for(si=System_Inputs;si!=NULL;si=si->next)
      for(mf=si->membership_functions;mf!=NULL;mf=mf->next)
     compute_degree_of_membership(mf,si->value);
}                                        /* END FUZZIFICATION */
rule_evaluation()
{  struct rule_type *rule;
   struct rule_element_type *ip;    /* if ptr */
   struct rule_element_type *tp;    /* then ptr */
   int strength;
   int nomatch=0;                   /* NEW, test some rules */
   for(rule=Rule_Base;rule!=NULL;rule=rule->next)
   {  strength=UPPER_LIMIT;
      for(ip=rule->if_side;ip!=NULL;ip=ip->next)
             strength=min(strength,*(ip->value));
      for(tp=rule->then_side;tp!=NULL;tp=tp->next)
      {  *(tp->value)=max(strength,*(tp->value));      /* NEW */
         if(strength>0)nomatch=1;                      /* NEW */
      }                                                /* NEW */
   }
   if(nomatch==0)printf("NO MATCHING RULES FOUND!\n"); /* NEW */
}                                        /* END RULE EVALUATION */
defuzzification()
{  struct io_type *so;
   struct mf_type *mf;
   int sum_of_products;
   int sum_of_areas;
   int area, centroid;
   for(so=System_Output;so!=NULL;so=so->next)
   {  sum_of_products=0;
      sum_of_areas=0;
      for(mf=so->membership_functions;mf!=NULL;mf=mf->next)
      {  area=compute_area_of_trapezoid(mf);
         centroid=mf->point1+(mf->point2-mf->point1)/2;
         sum_of_products+=area*centroid;
         sum_of_areas+=area;
      }
      if(sum_of_areas==0)                                    /* NEW */
      {  printf("Sum of Areas = 0, will cause div error\n"); /* NEW */
         printf("Sum of Products= %d\n",sum_of_products);    /* NEW */
         so->value=0;                                        /* NEW */
         return;                                             /* NEW */
      }                                                      /* NEW */
      so->value=sum_of_products/sum_of_areas;
   }
}                                        /* END DEFUZZIFICATION */
compute_degree_of_membership(mf,input)
struct mf_type *mf;
int input;
{  int delta_1, delta_2;
   delta_1=input - mf->point1;
   delta_2=mf->point2 - input;
   if((delta_1<=0)||(delta_2<=0))mf->value=0;
   else
   {  mf->value=min((mf->slope1*delta_1),(mf->slope2*delta_2));
      mf->value=min(mf->value,UPPER_LIMIT);
   }
}                                        /* END DEGREE OF MEMBERSHIP */
compute_area_of_trapezoid(mf)
struct mf_type *mf;
{  float run_1,run_2,area,top;
   float base;
   base=mf->point2 - mf->point1;
   run_1=mf->value / mf->slope1;
   run_2=mf->value / mf->slope2;
   top=base - run_1 - run_2;
   area=mf->value*(base+top)/2;
   return(area);
}                                        /* END AREA OF TRAPEZOID */
initialize_system()                      /* NEW FUNCTION INITIALIZE */
{  int a, b, c, d, x;
   char buff[10],buff1[4],buff2[4];
   static char filename1[]="in1";  /* "angles" filename */
   static char filename2[]="in2";  /* "velocities" filename */
   static char filename3[]="out1"; /* "forces" filename */
   FILE *fp;
   struct io_type *outptr;
   struct mf_type *top_mf;
   struct mf_type *mfptr;
   struct io_type *ioptr;
   struct rule_type *ruleptr;
   struct rule_element_type *ifptr;
   struct rule_element_type *thenptr;
   ioptr=NULL;
   ruleptr=NULL;
   ifptr=NULL;
   thenptr=NULL;
/* READ THE FIRST FUZZY SET (ANTECEDENT); INITIALIZE STRUCTURES */
   if((fp=fopen(filename1,"r"))==NULL)   /* open "angles" file */
   {  printf("ERROR- Unable to open data file named %s.\n",filename1);
      exit(0);
   }
   ioptr=(struct io_type *)calloc(1,sizeof(struct io_type));
   System_Inputs=ioptr;                  /* Anchor to top of inputs */
   x=fscanf(fp,"%s",buff);               /* from 1st line, get set's name */
   sprintf(ioptr->name,"%s",buff);       /* into struct io_type.name */
   mfptr=NULL;
   while((x=fscanf(fp,"%s %d %d %d %d",buff,&a,&b,&c,&d))!=EOF)/* get line */
   {  if(mfptr==NULL)                    /* first time thru only */
      {  mfptr=(struct mf_type *)calloc(1,sizeof(struct mf_type));
         top_mf=mfptr;
         ioptr->membership_functions=mfptr;
      }
      else
      {  for(mfptr=top_mf;mfptr->next;mfptr=mfptr->next); /* spin to last */
         mfptr->next=(struct mf_type *)calloc(1,sizeof(struct mf_type));
         mfptr=mfptr->next;
      }
      sprintf(mfptr->name,"%s",buff);    /* membership name, NL, ZE, etc */
      mfptr->point1=a;                   /* left x axis value */
      mfptr->point2=d;                   /* right x axis value */
      if(b-a>0) mfptr->slope1=UPPER_LIMIT/(b-a);     /* left slope */
      else
      {  printf("Error in input file %s, membership element %s.\n",
         filename1,buff);
         exit(1);
      }
      if(d-c>0) mfptr->slope2=UPPER_LIMIT/(d-c);     /* right slope */
      else
      {  printf("Error in input file %s, membership element %s.\n",
         filename1,buff);
         exit(1);
      }
   }
   close(fp);                            /* close "angles" file */
/* READ THE SECOND FUZZY SET (ANTECEDENT); INITIALIZE STRUCTURES */
   if((fp=fopen(filename2,"r"))==NULL)   /* open "velocity" file */
   {  printf("ERROR- Unable to open data file named %s.\n",filename2);
      exit(0);
   }
   ioptr->next=(struct io_type *)calloc(1,sizeof(struct io_type));
   ioptr=ioptr->next;
   x=fscanf(fp,"%s",buff);               /* from 1st line, get set's name */
   sprintf(ioptr->name,"%s",buff);       /* into struct io_type.name */
   mfptr=NULL;
   while((x=fscanf(fp,"%s %d %d %d %d",buff,&a,&b,&c,&d))!=EOF)/* get line */
   {  if(mfptr==NULL)                    /* first time thru only */
      {  mfptr=(struct mf_type *)calloc(1,sizeof(struct mf_type));
         top_mf=mfptr;
         ioptr->membership_functions=mfptr;
      }
      else
      {  for(mfptr=top_mf;mfptr->next;mfptr=mfptr->next); /* spin to last */
         mfptr->next=(struct mf_type *)calloc(1,sizeof(struct mf_type));
         mfptr=mfptr->next;
      }
      sprintf(mfptr->name,"%s",buff);    /* membership name, NL, ZE, etc */
      mfptr->point1=a;                   /* left x axis value */
      mfptr->point2=d;                   /* right x axis value */
      if(b-a>0) mfptr->slope1=UPPER_LIMIT/(b-a);     /* left slope */
      else
      {  printf("Error in input file %s, membership element %s.\n",
         filename2,buff);
         exit(1);
      }
      if(d-c>0) mfptr->slope2=UPPER_LIMIT/(d-c);     /* right slope */
      else
      {  printf("Error in input file %s, membership element %s.\n",
         filename2,buff);
         exit(1);
      }
   }
   close(fp);                            /* close "velocity" file */
/* READ THE THIRD FUZZY SET (CONSEQUENCE); INITIALIZE STRUCTURES */
   if((fp=fopen(filename3,"r"))==NULL)   /* open "force" file */
   {  printf("ERROR- Unable to open data file named %s.\n",filename3);
      exit(0);
   }
   ioptr=(struct io_type *)calloc(1,sizeof(struct io_type));
   System_Output=ioptr;                  /* Anchor output structure */
   x=fscanf(fp,"%s",buff);               /* from 1st line, get set's name */
   sprintf(ioptr->name,"%s",buff);       /* into struct io_type.name */
   mfptr=NULL;
   while((x=fscanf(fp,"%s %d %d %d %d",buff,&a,&b,&c,&d))!=EOF)/* get line */
   {  if(mfptr==NULL)                    /* first time thru */
      {  mfptr=(struct mf_type *)calloc(1,sizeof(struct mf_type));
         top_mf=mfptr;
         ioptr->membership_functions=mfptr;
      }
      else
      {  for(mfptr=top_mf;mfptr->next;mfptr=mfptr->next);
         mfptr->next=(struct mf_type *)calloc(1,sizeof(struct mf_type));
         mfptr=mfptr->next;
      }
      sprintf(mfptr->name,"%s",buff);    /* membership name, NL, ZE, etc */
      mfptr->point1=a;                   /* left x axis value */
      mfptr->point2=d;                   /* right x axis value */
      if(b-a>0) mfptr->slope1=UPPER_LIMIT/(b-a);     /* left slope */
      else
      {  printf("Error in input file %s, membership element %s.\n",
         filename3,buff);
         exit(1);
      }
      if(d-c>0) mfptr->slope2=UPPER_LIMIT/(d-c);     /* right slope */
      else
      {  printf("Error in input file %s, membership element %s.\n",
         filename3,buff);
         exit(1);
      }
   }
   close(fp);                            /* close "force" file */
/* READ RULES FILE; INITIALIZE STRUCTURES */
   ioptr=NULL;
   outptr=NULL;
   if((fp=fopen("rules","r"))==NULL)     /* open rules file */
   {  printf("ERROR- Unable to open data file named %s.\n","rules");
      exit(0);
   }
   ruleptr=(struct rule_type *)calloc(1,sizeof(struct rule_type));
   if(ioptr==NULL)Rule_Base=ruleptr;     /* first time thru, anchor */
   while((x=fscanf(fp,"%s %s %s",buff,buff1,buff2))!=EOF) /* get a line */
   {  ioptr=System_Inputs;               /* points to angle */
      for(mfptr=ioptr->membership_functions;mfptr!=NULL;mfptr=mfptr->next)
      {  if((strcmp(mfptr->name,buff))==0)
     {  ifptr=(struct rule_element_type *)
                  calloc(1,sizeof(struct rule_element_type));
        ruleptr->if_side=ifptr;      /* points to angle */
        ifptr->value=&mfptr->value;  /* needs address here */
        ifptr->next=(struct rule_element_type *)
                  calloc(1,sizeof(struct rule_element_type));
        ifptr=ifptr->next;
        break;                       /* match found */
          }
      }
      ioptr=ioptr->next;                 /* points to velocity */
      for(mfptr=ioptr->membership_functions;mfptr!=NULL;mfptr=mfptr->next)
      {  if((strcmp(mfptr->name,buff1))==0)
     {  ifptr->value=&mfptr->value;  /* needs address here */
        break;                       /* match found */
          }
      }
      if(outptr==NULL)outptr=System_Output;/* point then stuff to output */
      for(mfptr=outptr->membership_functions;mfptr!=NULL;mfptr=mfptr->next)
      {  if((strcmp(mfptr->name,buff2))==0)
         {  thenptr=(struct rule_element_type *)
                calloc(1,sizeof(struct rule_element_type));
        ruleptr->then_side=thenptr;
            thenptr->value=&mfptr->value; /* needs address here */
        break;                        /* match found */
         }
      }
      ruleptr->next=(struct rule_type *)calloc(1,sizeof(struct rule_type));
      ruleptr=ruleptr->next;
   }                                     /* END WHILE READING RULES FILE */
   close(fp);                            /* close "rules" file */
}                                        /* END INITIALIZE */
put_system_outputs()                     /* NEW */
{  struct io_type *ioptr;
   struct mf_type *mfptr;
   struct rule_type *ruleptr;
   struct rule_element_type *ifptr;
   struct rule_element_type *thenptr;
   int cnt=1;
   for(ioptr=System_Inputs;ioptr!=NULL;ioptr=ioptr->next)
   {  printf("%s: Value= %d\n",ioptr->name,ioptr->value);
      for(mfptr=ioptr->membership_functions;mfptr!=NULL;mfptr=mfptr->next)
      {  printf("  %s: Value %d Left %d Right %d\n",
           mfptr->name,mfptr->value,mfptr->point1,mfptr->point2);
      }
     printf("\n");
   }
   for(ioptr=System_Output;ioptr!=NULL;ioptr=ioptr->next)
   {  printf("%s: Value= %d\n",ioptr->name,ioptr->value);
      for(mfptr=ioptr->membership_functions;mfptr!=NULL;mfptr=mfptr->next)
      {  printf("  %s: Value %d Left %d Right %d\n",
           mfptr->name,mfptr->value,mfptr->point1,mfptr->point2);
      }
   }
/* print values pointed to by rule_type (if & then) */
   printf("\n");
   for(ruleptr=Rule_Base;ruleptr->next!=NULL;ruleptr=ruleptr->next)
   {  printf("Rule #%d:",cnt++);
      for(ifptr=ruleptr->if_side;ifptr!=NULL;ifptr=ifptr->next)
          printf("  %d",*(ifptr->value));
      for(thenptr=ruleptr->then_side;thenptr!=NULL;thenptr=thenptr->next)
          printf("  %d\n",*(thenptr->value));
   }
   printf("\n");
}                                        /* END PUT SYSTEM OUTPUTS */
get_system_inputs(input1,input2)         /* NEW */
int input1, input2;
{  struct io_type *ioptr;
   ioptr=System_Inputs;
   ioptr->value=input1;
   ioptr=ioptr->next;
   ioptr->value=input2;
}                                        /* END GET SYSTEM INPUTS */

Copyright © 1994, Dr. Dobb's Journal