Supplementary Library Routines

Routines from the fileio library

eoln

boolean eoln (FILE *f)

Returns TRUE if the file pointer is currently at the end of the input line or FALSE otherwise. The argument f  must be a pointer to a file structure that has been successfully opened for reading. Note that if a program reads from an input file character-by-character and reads the end-of-line sequence (generally either a carriage return character or a carriage return and line feed character), the eoln function will return FALSE, since it determines that the end of line has been reached by detecting the end-of-line sequence. It does not discard the end-of-line character(s), so to proceed to the next line of the file, you should call nextLine before the next read operation.

Example:

  #include <fileio.h>
  #include <stdio.h>
  #include <stdlib.h>
  main ()
  { 
    FILE *in;
    int number;
    if ((in = fopen ("data.txt", "rt")) == NULL) 
      perror ("Could not open input file data.txt");
      exit (1);
    }  
    /* read all numbers on the first line and print their squares */
    while (!eoln (in)) { 
      fscanf (in, "%d", &number);
      printf ("%d squared is %d\n", number * number);
    }
    fclose (in);
  }

fileEnd

boolean fileEnd (FILE *f)

 

Returns TRUE if the file pointer is currently at the end of the input file or FALSE otherwise. The argument f  must be a pointer to a file structure that has been successfully opened for reading.

Example:

  #include <fileio.h>
  #include <stdio.h>
  #include <stdlib.h>
  main ()
  { 
    FILE *in;
    int number;
    if ((in = fopen ("data.txt", "rt")) == NULL) 
      perror ("Could not open input file data.txt");
      exit (1);
    }  
    /* read all numbers in the input file and print their squares */
    while (!fileEnd (in))
      fscanf (in, "%d", &number);
      printf ("%d squared is %d\n", number * number);
    }
    fclose (in);
  }      

nextLine

void nextLine (FILE *f)

Moves the file pointer to the end of the current line of the input file so that the next read operation will read from the next line (if one exists) of the file. This is useful for discarding extraneous input on a line of an input file, but also for looping through a file line-by-line in conjunction with the fileEnd and eoln functions.

Example:

#include <stdio.h>
#include <stdlib.h>
#include <fileio.h>
main ()
{
    FILE *in;
    char ch;
    int charCount, lineCount = 0;
    if ((in = fopen ("data.txt", "rt")) == NULL) 
      perror ("Could not open input file data.txt");
      exit (1);
    } 
    while (!fileEnd (in)) {
      charCount = 0;
      while (!eoln (in)) {
        fscanf (in, " %c", &ch);
        ++charCount;
      }
      ++lineCount;
      printf ("Line %d contained %d non-blank characters\n", 
               lineCount, charCount); 
      nextLine (in);
    }
    fclose (in);
}

Routines from the sets library

Notes on the mathematical notion of sets:
A set is a mathematical abstraction which denotes an unordered collection of items. A member or element of a set appears only once in the set, that is, a set contains no duplicates. The standard mathematical notation for a set uses braces to surround a list of the elements of the set. For example, {1 2 3} denotes the set of integers greater than 0 and less than 4. Since sets are unordered, the set {1 2 3} and the set {3 2 1} are identical. Most often, a set contains values from a given base or component type. The example given has a component type of integer. The universe set is the set that contains all the possible values of a particular component type. Obviously, in mathematical terms, it is impossible to list the universe set whose base type is integer, since it contains an infinite number of members. We could certainly list the universe set whose base type is capital letters: {A B C D E F G H I J K L M N O P Q R S T U V W X Y Z}.  The number of  members that a set contains is its cardinality. Thus the set in the first example, {1 2 3}, has a cardinality of three, the universe set of integers has infinite cardinality, and the set of all capital letters has a cardinality of 26. The empty set, a set with no members, denoted as {} or phi, has a cardinality of zero.

A description of the most common set operations follows:

  1. union
    The union of two sets results in a set that contains all the elements of both sets. For example, the union of the sets {1 2 3} and {3 4 5} gives the set {1 2 3 4 5}. (Note that since a set does not contain duplicates, the 3 appears only once, even though it was a member of both the original sets.
  2. intersection
    The intersection of two sets results in a set that contains just those elements that are members of both the original sets, that is, the common members of the two sets. For example, the intersection of the sets {1 2 3} and {3 4 5} gives the set {3}.
  3. difference
    Given two sets s1 and s2, the difference s1 - s2 gives the set of all elements in s1 that are not members of s2. For example, if s1 is {1 2 3 4 5} and s2 is {2 5}, s1 - s2 is {1 3 4}. If the second operand contains elements that are not members of the first operand, they plainly do not affect the operation. Thus, if s2 is {2 5 6}, s1 - s2 is still {1 3 4}.
  4. subset
    We say a set s1 is a subset of another set s2 if all the elements of s1 are also members of s2. For example, {1 2 3} is a subset of {0 1 2 3 4}. Further, {1 2 3} is also a subset of {1 2 3}. By definition, the empty set is a subset of every set.
  5. proper subset
    We say a set s1 is a proper subset of s2 if s1 is a subset of s2 and s2 - s1 is not the empty set. In plain language, s2 must contain at least one element that is not a member of s1.
  6. equality
    We say two sets are equal when they have exactly the same elements. Thus, {1 2 3} and {3 2 1} are equal. Equivalently, we can say two sets s1 and s2 are equal when s1 is a subset of s2 and s2 is a subset of s1.
  7. superset, proper superset
    These are inverse operations for subset and proper subset. If s1 is a subset of s2, then s2 is a superset of s1. In other words, s1 is a superset of s2 if s1 contains all the elements of s2. We say s1 is a proper superset of s2 if s1 contains all the elements of s2 in addition to at least one more element.

Notes on sets as implemented in the sets library

Because of the inherent limitations of computers and for reasons of simplicity of implementation and efficiency, the set data type implemented in the set library differs somewhat from the mathematical notion of sets. First, all elements of a set must be of the same data type and only certain component types are permissible. The component types for sets are int, char, and enumerated types. Specifically, sets cannot contain members of type float. Furthermore, although components may be integers, the universe set of integers contains only numbers between 0 and 1023.

The sets library declares a type name for set variables: set. Note that this type name does not specify the component type of the elements of the set. The component type is implicit, determined by the elements that you add to the set variable. To declare a set variable, you may use the type name just as you would any other type name. For example:
set mySet;
declares a set variable named mySet which you could use to contain elements of type int, char, or an enumerated type.

When you declare a variable of type set, it contains garbage, just as any other declared variable. Before you use the set, you must initialize it. Two operations, emptySet and addElems serve to initialize a set variable. The library also includes a print operation to print a set, but if the base type is an enumerated type, the print routine will print the elements as integers, rather than as the symbolic enumerated type values, so in this case, it would be better to write your own print routine to print each element of the set.

You should also take special note of the fact that although the braces used to denote sets in mathematics also exist in C, you cannot use braces in C to specify a set. In other words, be careful to avoid the temptation of trying to assign a value to a set variable using a statement like mySet = {1 2 3}; since it will not work. 

Operations

emptySet

set emptySet ()

Creates and returns a set with no elements.

Example:

#include <sets.h>
main ()
{
  set mySet = emptySet (); /* initializes mySet to the empty state */
  ...
}

addElems

set addElems (set s, int numElems, ...)

Adds numElems elements to an existing set s and returns the resulting set. This routine is mainly useful for initializing a set that must contain certain elements. The ... argument in the function signature stands for a list of parameters that specify the elements to add to the set. It is essential that the number of parameters in this list be exactly the same as the numElems parameter or the results will be unpredictable and undesirable. addElems does not alter the input parameter s, so it is important to assign the result of the function call to a set variable.

Example:

#include <sets.h>
main ()
{
  set mySet = addElems (emptySet (), 3, 'a', 'b', 'c');
  /* initialize mySet to the empty set and add elements 'a', 'b', and 'c'.
     after this line executes, mySet is {'a' 'b' 'c'} */
  ...
}

setUnion

set setUnion (set s1, set s2)

Returns the union of sets s1 and s2.

Example:

#include <sets.h>
main ()
{
  set s1 = addElems (emptySet (), 3, 'a', 'b', 'c'), 
      s2 = addElems (emptySet (), 4, 'a', 'c', 'e', 'g'),
      s3;
  s3 = setUnion (s1, s2); /*s3 is now {'a' 'b' 'c' 'e' 'g'} */
  /*s1 and s2 remain unaltered */
}

intersection

set intersection (set s1, set s2)

Returns the intersection of sets s1 and s2.

Example:

#include <sets.h>
main ()
{
  set s1 = addElems (emptySet (), 3, 'a', 'b', 'c'), 
      s2 = addElems (emptySet (), 4, 'a', 'c', 'e', 'g'),
      s3;
  s3 = intersection (s1, s2); /*s3 is now {'a' 'c'} */
  /*s1 and s2 remain unaltered */
}

difference

set difference (set s1, set s2)

Returns the difference s1 - s2.

Example:

#include <sets.h>
main ()
{
  set s1 = addElems (emptySet (), 3, 'a', 'b', 'c'), 
      s2 = addElems (emptySet (), 4, 'a', 'c', 'e', 'g'),
      s3;
  s3 = difference (s2, s1); /*s3 is now {'e' 'g'} */
  /*s1 and s2 remain unaltered */
}

member

boolean member (set s, elem)

Returns TRUE if elem is a member of set s or FALSE otherwise. Since the component types of sets are implicit, it is possible for member to return TRUE when logically it should return FALSE. This can happen if elem is of a different type from the members of set s, but the underlying representation of elem and a member of s are the same. For instance, if s is the set {'a' 'b' 'c'}, the call member (s, 97); will return TRUE, since 97 is the ASCII code for 'a'.

Example:

#include <sets.h>
#typedef enum {dog, cat, bird, goldfish, hamster} PetType;
main ()
{
  set s1 = addElems (emptySet (), 3, dog, cat, hamster), 
      s2 = addElems (emptySet (), 2, goldfish, bird),
      s3;
  PetType pet;
  s3 = difference (s1, s2);
  /* print the elements of s3: */
  printf ("Set s3: { ");
  for (pet = dog, pet <= hamster; ++pet) {
    if (member (s3, pet))
       switch (pet) {
         case dog:      printf ("dog ");      break;
         case cat:      printf ("cat ");      break;
         case bird:     printf ("bird ");     break;
         case goldfish: printf ("goldfish "); break;
         case hamster:  printf ("hamster ");  
       }
  }
  printf ("}\n");
}

equal

boolean equal (set s1, set s2)

Returns TRUE if s1 and s2 contain exactly the same elements. Since the base type of a set variable is implicit, it is important to use this function with care, since it may return TRUE if the arguments of the function  contain elements that are of different base types but happen to have the same underlying representation. For example, if s1 is the set {'a' 'b' 'c'} and s2 is the set {97 98 99}, equal will return TRUE, since 97, 98, and 99 are the ASCII codes for the characters 'a', 'b', and 'c', respectively.

Example:

#include <sets.h>
main () 
{
  set s1, s2;
  ... /* initialize s1 and s2 */
  if (equal (s1, s2)) 
    printf ("Sets s1 and s2 are the same\n");
  else printf ("Sets s1 and s2 are different\n");
}

subset

boolean subset (set s1, set s2)

Returns TRUE if s1 is a subset of s2 or FALSE otherwise. The same precaution regarding component types applies here as for the member and equal functions.

Example:

#include <sets.h>
main () 
{
  set s1, s2;
  ... /* initialize s1 and s2 */
  if (subset (s1, s2)) 
    printf ("Set s1 is a subset of s2\n");
  else printf ("Set s1 is not a subset of s2\n");
}

properSubset

boolean properSubset (set s1, set s2)

Returns TRUE if s1 is a proper subset of s2 or FALSE otherwise. The same precaution regarding component types applies here as for the member and equal functions.

Example:

#include <sets.h>
main () 
{
  set s1, s2;
  ... /* initialize s1 and s2 */
  if (properSubset (s1, s2)) 
    printf ("Set s1 is a proper subset of s2\n");
  else printf ("Set s1 is not a proper subset of s2\n");
}

NOTE: You can use the subset and properSubset functions to determine whether one set is a (proper) superset of another by reversing the order of the arguments.

printSet

void printSet (set s, elemType t)

Prints the value of a set variable using brace notation. The argument t must be one of the following values: INT, CHAR, or ENUM and serves to indicate the component type of the elements of the set. Note that if the elements of the set are of an enumerated type, printSet will print the elements as integers. Thus, if you wish to print a set that contains members of an enumerated type, it is better to write your own print routine. The example for member shows a model for such a routine.

Example:

#include <sets.h>
main () 
{
  set mySet = addElems (emptySet (), 4, 1, 10, 25, 6);
  printSet (mySet, INT); /* prints mySet as "{ 1 6 10 25 }" */
}