Sunday, July 29, 2012

The Generic Data Structures Library

The purpose of the GDS (Generic Data Structures) Library is to provide type-independent data structures such as lists, sets, trees or arrays for the C programming language.

A GDS object contains 2 data structures:
-A interface data structure which contains the object's generic behaviors:
      -Copy (Used for creating a hard copy of the object)
      -Destroy (Used for freeing the memory occupied by the object)
      -Compare (Used for comparing 2 objects)
-A generic pointer to another structure which should hold the object's attributes.

The implementation of the GDS object:
/*Contains the attributes and the behaviors of the object*/
typedef struct
{
   /*Contains the object's data*/
   void    *data;
   /*Contains pointers to the object's function*/
   Interface  *interface;
}Object;
The interface data structure is defined in the following way:
/*Contains pointers to the object's functions*/
typedef struct
{
   /*A pointer to a hard copy function*/
   void* (*Copy)(const void*);
   /*A pointer to a destructor-like function*/
   void* (*Destroy)(void*);
   /*A pointer to a compare function*/
   int   (*Compare)(const void*,const void*);
}Interface;
The object data structure uses the following functions:
/*
 * Description:
 *  Creates an Object structure
 * Parameters:
 *  value - The value used for initializing an object
 * Returns:
 *  A pointer to the object OR
 *  NULL if there is not enough memory
 */
Object* Object_Create(void* value, void* interface);
/*
 * Description:
 *  Compares two objects
 * Parameters:
 *  object1, object2 - the objects who are going to be compared
 * Returns:
 *  The difference between the values of the objects
 *  0  - the objects are equal
 *  >0 - object1 is greater than object2
 *  <0 - object2 is greater than object1
 */
int Object_Compare(const Object* element1, const Object* element2);
/*
 * Description:
 *  Creates a hard copy of an object structure
 * Parameters:
 *  value - A pointer to object who is going to be copied
 * Returns:
 *  A pointer to the copy OR
 *  NULL if there is not enough memory
 */
Object* Object_Copy(const Object* element);
/*
 * Description:
 *  Destroys an object structure
 * Parameters:
 *  object - The object who is going to be destroyed
 * Returns:
 * NULL
 */
Object* Object_Destroy(Object* element);
The interface data structure has the following functions:
/*
 * Description:
 *  Creates an Interface structure
 * Parameters:
 *  Copy - a pointer to the object copy function
 *  Destroy - a pointer to the object destructor
 *  Compare - a pointer to the object compare function
 *  GetType - reserved for further use
 * Returns:
 * NULL
 */
Interface* Interface_Create(void* (*Copy)(const void*),
                            void* (*Destroy)(void*),
                            int (*Compare)(const void*,const void*));
/*
 * Description:
 *  Destroys an Interface structure
 * Parameters:
 *  interface - a pointer to a interface structure
 * Returns:
 *  NULL
 */
Interface* Interface_Destroy(Interface* interface);

Friday, July 27, 2012

Temperature Conversion Library in Java

This library allows you to perform conversion between the Celsius, Kelvin, Rankine, Delisile, Newton, Reaumur, Romer and Fahrenheit temperature scales.

Here's an example on how to use the library:
package temperatureconversion;

public class TestLauncher
{
    public static void main(String[] args)
    {
        Temperature t1 = Temperature.fromCelsius(-273.15);
        Temperature t2 = Temperature.fromKelvin(0.0);
        System.out.println("Kelvin degrees    : " + t1.toKelvin()     +"\n" +
                           "Celsius degrees   : " + t1.toCelsius()    +"\n" +
                           "Delisle degrees   : " + t1.toDelisle()    +"\n" +
                           "Newton degrees    : " + t1.toNewton()     +"\n" +
                           "Rankine degrees   : " + t1.toRankine()    +"\n" +
                           "Reaumur degrees   : " + t1.toReaumur()    +"\n" +
                           "Fahrenheit degrees: " + t1.toFahrenheit() +"\n" +
                           "Romer degrees     : " + t1.toRomer()      +"\n" );
        System.out.println("Kelvin degrees    : " + t2.toKelvin()     +"\n" +
                           "Celsius degrees   : " + t2.toCelsius()    +"\n" +
                           "Delisle degrees   : " + t2.toDelisle()    +"\n" +
                           "Newton degrees    : " + t2.toNewton()     +"\n" +
                           "Rankine degrees   : " + t2.toRankine()    +"\n" +
                           "Reaumur degrees   : " + t2.toReaumur()    +"\n" +
                           "Fahrenheit degrees: " + t2.toFahrenheit() +"\n" +
                           "Romer degrees     : " + t2.toRomer()      +"\n" );
    }
}

Designing Applets with the Netbeans GUI Designer

If you want to use the Netbeans GUI editor for an Applet you must follow the next steps:
1. In the menu bar go to File -> New Project and then select Java->Java Application
2.Name your application and select a location for the project
3, Select the package where you want to add your applet and then right click -> New -> Other. After that go to Swing GUI Forms -> JApplet Form
4.Choose a name for your applet and press Finish
5. Double click on the newly created file in the Project view. You can easily toggle between the source and design view of the file by clicking the highlighted button in the picture above.
Enjoy designing applets with Netbeans!

Pangram Checking Algorithm in ANSI C

A pangram is a sentence that contains at least once all the letters of the alphabet. A well known pangram in the English language is the sentence "A quick brown fox jumps over the lazy dog".

To check if a string is a pangram we must implement a flag vector containing entries for each letter of the alphabet. A flag will be set if the corresponding letter is found in the string. If all flags from the flag vector are set, then the string is pangram.

An implementation for this function would be:
/*
 * Description:
 *  Verifies is the string is a pangram
 * Parameters:
 *  string - a pointer to the string
 * Returns:
 *  true - if the word is a pangram
 *  false - if the word is not a pangram
 */
bool IsPangram(char* string)
{
   bool alphabetFlags[NUMBER_OF_LETTERS];
   int size = strlen(string);
   bool isPangram = true;
   char c;
   int i;

   /*Initializes all alphabet flags to false*/
   for(i=0; i<NUMBER_OF_LETTERS; i++)
   {
      alphabetFlags[i] = false;
   }
   /*For every unique letter the string contains an
    alphabet flag will be set to true*/
   for(i=0; i<size; i++)
   {
      c = tolower(string[i]);
      if(islower(c))
      {
         alphabetFlags[string[i]-'a']=true;
      }
   }
   /*If the string is a pangram every flag will be set to true*/
   for(i=0; (i<NUMBER_OF_LETTERS && isPangram==true); i++)
   {
      if(alphabetFlags[i]==false)
      {
         isPangram=false;
      }
   }
   return isPangram;
}
The function initializes all flags from the bool vector to false. After that, it checks every letter character from the string and sets the corresponding flag in the bool vector. If all flags are set, then the string is pangram.

Example
#include<stdio.h>
#include<string.h>
#include<stdbool.h>
#include<ctype.h>

#define NUMBER_OF_LETTERS 26

bool IsPangram(char* string);

int main(void)
{
   char pangram1[] = "The quick brown fox jumps over the lazy dog";
   char pangram2[] = "Bird, bird, bird! The bird is the word!";
   if(IsPangram(pangram1))
   {
      printf("<%s> is a pangram\n",pangram1);
   }
   else
   {
      printf("<%s> is not a pangram\n",pangram1);
   }
   if(IsPangram(pangram2))
   {
      printf("<%s> is a pangram\n",pangram2);
   }
   else
   {
      printf("<%s> is not a pangram\n",pangram2);
   }
   return 0;
}
/*Output
<The quick brown fox jumps over the lazy dog> is a pangram
<Bird, bird, bird! The bird is the word!> is not a pangram
 */

Anagram Checking Algorithm in ANSI C

An anagram is a word or phrase formed by reordering the letters of another word or phrase [Free Online Dictionary].

The easiest method to check if a string is an anagram of another string is to build the letter frequency statistics for both strings and compare them. If the statistics vectors are equal, then the strings are anagrams.

An implementation of such function is:
/*
 * Description:
 *  Checks if @anagram string is an anagram of the @original string
 * Parameters:
 *  original - the string to which @anagram will be compared to
 *  anagram  - the string who is going to be verified if its an anagram
 * Returns:
 *  true - @anagram is an anagram of @original
 *  false - @anagram is not a anagram of @original
 * Preconditions:
 *  @anagram and @original should only contain letters
 */
bool IsAnagram(const char* original, const char* anagram)
{
   /*Allocates on the stack two character vectors who shall
    be populated with the character statistics from the
    two sent strings*/
    int originalStats[NUMBER_OF_LETTERS];
    int anagramStats[NUMBER_OF_LETTERS];
    /*Computes the length of the sent strings*/
    int originalSize = strlen(original);
    int anagramSize = strlen(anagram);
    int i;
    char temp;
    bool isAnagram = true;

    /*Initializes the statistics vectors*/
    for(i=0; i<NUMBER_OF_LETTERS;i++)
    {
       originalStats[i] = 0;
       anagramStats[i] = 0;
    }
    /*Builds the stats for @anagram and original. If a non-letter
    is encountered, then the function will return false*/
    for(i=0; i<originalSize;i++)
    {
       temp = tolower(original[i]);
       if(islower(temp))
       {
          originalStats[temp-'a']++;
       }
    }
    for(i=0; i<anagramSize;i++)
    {
       temp = tolower(anagram[i]);
       if(islower(temp))
       {
          anagramStats[temp-'a']++;
       }
    }
    /*Compares the stats of the two strings. If the vectors are not
     identical, then @anagram is not an anagram of @original*/
    for(i=0; (i<NUMBER_OF_LETTERS && isAnagram==true); i++)
    {
       if(originalStats[i]!=anagramStats[i])
       {
          isAnagram = false;
       }
    }
    return isAnagram;
}
The function first builds the letter frequency vectors for both strings, counting only and only the letters (punctuation and whitespace are not relevant). All letters are transformed to lowercase. After that the function compares the statistics vector element by element. If two elements having the same index (corresponding to a letter) are not equal, then the strings are not anagrams.

Example:
#include<stdio.h>
#include<ctype.h>
#include<string.h>
#include<stdbool.h>

#define STRING_SIZE 50
#define NUMBER_OF_LETTERS 26

bool IsAnagram(const char* original, const char* anagram);


int main(void)
{
   /*The original string*/
   char original[]="Clint Eastwood";
   /*An anagram of the original string*/
   char anagram1[]="Old West Action";
   /*A string who's not an anagram of the original*/
   char anagram2[]="Westwood Action";
   if(IsAnagram(original,anagram1))
   {
      printf("%s is an anagram of %s\n",anagram1, original);
   }
   else
   {
      printf("%s is not an anagram of %s\n",anagram1, original);
   }
   if(IsAnagram(original,anagram2))
   {
      printf("%s is an anagram of %s\n",anagram2, original);
   }
   else
   {
      printf("%s is not an anagram of %s\n",anagram2, original);
   }
   return 0;
}
/*Output:
 Old West Action is an anagram of Clint Eastwood
 Westwood Action is not an anagram of Clint Eastwood
 */

Thursday, July 26, 2012

Character Frequency and Histogram in ANSI C

In order to understand this program you must know how to operate with text files and external arguments. If you don't, the following articles may prove helpful:
1. Computing the character frequency
/*
 * Description:
 *  Computes for each character the number of occurrences in the file
 *  specified by @stream.
 * Parameters:
 *  stream - a pointer to file
 *  statistics - a pointer to a vector containing the number of occurrences
 *      of each character.
 *  Returns:
 *   Nothing
 */
void DoStatistics(FILE* stream, long* statistics)
{
   /*Initializes the variable with an arbitrary value in order to avoid
    the use of a do-while loop*/
   char temp = 0x01;
   int i;
   /*Initializes the statistics vector*/
   for(i = 0; i<UCHAR_MAX; i++)
   {
      statistics[i] = 0L;
   }
   /*Computes the number of occurences for each character*/
   while(temp!=EOF)
   {
      temp = fgetc(stream);
      statistics[(int)(temp)]++;
   }
}
/*
 * Description:
 *  Outputs to console the character statistics in the format:
 *  character <number of occurrences>. Only printable characters will
 *  be taken into consideration.
 * Parameters:
 *  statistics - a pointer to a vector containing the number of occurrences
 *      of each character.
 *  printAll - if true, all characters will appear. Otherwise only character
 *       with at least one occurrence will appear.
 *  Returns:
 *   Nothing
 */
void PrintStatistics(long* statistics, bool printAll)
{
   int i;
   for(i = 0; i<UCHAR_MAX; i++)
   {
      if(isprint(i) && ( (statistics[i]!=0) || (printAll==true) ) )
      {
         printf("%c <%ld>\n",(char)(i),statistics[i]);
      }
   }
}
In order to compute the character frequency from a file we shall use the functions above. The first will read the file character by character and populate the statistics vector. The statistics vector size should be UCHAR_MAX. The second one function will be used for printing the character frequency statistics and should receive as a parameter a pointer to the vector populated by the first function.

2.Printing the Statistics as a Histogram
/*
 * Description:
 *  Outputs to console the character statistics as a histogram.
 *  Only printable characters will be taken into consideration.
 * Parameters:
 *  statistics - a pointer to a vector containing the number of occurrences
 *      of each character.
 *  maxScaleValue - the maximum number of asterisks that could appear in
 *      the histogram. The frequency of a character in the
 *      statistics vector is scaled according to this value.
 *  printAll - if true, all characters will appear in the histogram.
 *       Otherwise only character with at least one occurrence will
 *       appear.
 *  Returns:
 *   Nothing
 */
void PrintHorizontalHistogram(long* statistics, int maxScaleValue, bool printAll)
{
   long asterisks = 0;
   long maxValue = 0;
   int i, j;
   /*Finds out the character who has the maximum number of occurrences.
    This value will be used for scaling the statistics values*/
   for(i = 0; i<UCHAR_MAX; i++)
   {
      if(isprint(i))
      {
         if(statistics[i]>maxValue)
         {
            maxValue = statistics[i];
         }
      }
   }
   /*Checks if the statistics vector is not empty*/
   if(maxValue!=0)
   {
      for(i = 0; i<UCHAR_MAX; i++)
      {
         /*Will output information only for characters with at least
         one occurrence if the printAll option is not set. Otherwise
         will output information for all printable characters*/
         if(isprint(i) && ( (statistics[i]!=0) || (printAll==true) ) )
         {
            /*Computes the number of asterisks that will be printed.*/
            asterisks = (statistics[i]*maxScaleValue)/ maxValue;
            /*Prints the character*/
            putchar(i);
            putchar(' ');
            /*Prints a number of asterisks proportional with the number
            of occurrences*/
            for(j = 0; j<asterisks; j++)
            {
               putchar('*');
            }
            putchar('\n');
         }
      }  
   }
   else
   {
      puts("The statistics vector is empty");
   }
}
This function will print the character frequency statistics as a horizontal histogram.

3.Example
#include <stdio.h>
#include <stdbool.h>
#include <limits.h>
#include <ctype.h>

#define NR_ARGS        2
#define FILE_ARG_INDEX 1

void DoStatistics(FILE* stream, long* statistics);
void PrintStatistics(long* statistics, bool printAll);
void PrintHorizontalHistogram(long* statistics, int maxScaleValue, bool printAll);

/*
 * Description:
 *  The program will output the number of occurrences of all printable
 * characters in file.
 */
int main(int argc, char** argv)
{
    FILE* stream = NULL;
    long statistics[UCHAR_MAX];
    if(argc==NR_ARGS)
    {
       stream = fopen(argv[FILE_ARG_INDEX],"r");
       if(stream!=NULL)
       {
          DoStatistics(stream,statistics);
          PrintStatistics(statistics,false);
          PrintHorizontalHistogram(statistics,70,false);
       }
       else
       {
          perror("Could not open file");
       }
    }
    else
    {
       perror("Incorrect number of arguments");
    }
    return 0;
}

The example program above will open a text file and store the character frequency statistics in the statistics vector. After that it will print the character frequency values and build a histogram.

If we consider a file with the following text:

Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of "de Finibus Bonorum et Malorum" (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, "Lorem ipsum dolor sit amet..", comes from a line in section 1.10.32. The standard chunk of Lorem Ipsum used since the 1500s is reproduced below for those interested. Sections 1.10.32 and 1.10.33 from "de Finibus Bonorum et Malorum" by Cicero are also reproduced in their exact original form, accompanied by English versions from the 1914 translation by H. Rackham.

The output of the program for a file containing this text will be:
  <174>
" <6>
( <1>
) <1>
, <13>
- <1>
. <21>
0 <10>
1 <13>
2 <4>
3 <7>
4 <3>
5 <3>
9 <1>
B <4>
C <7>
E <3>
F <2>
G <1>
H <2>
I <6>
L <9>
M <3>
R <3>
S <2>
T <4>
V <1>
a <50>
b <12>
c <31>
d <29>
e <85>
f <19>
g <10>
h <23>
i <59>
k <6>
l <27>
m <34>
n <49>
o <79>
p <19>
r <63>
s <54>
t <53>
u <28>
v <5>
w <4>
x <3>
y <11>
  **********************************************************************
" **
(
)
, *****
-
. ********
0 ****
1 *****
2 *
3 **
4 *
5 *
9
B *
C **
E *
F
G
H
I **
L ***
M *
R *
S
T *
V
a ********************
b ****
c ************
d ***********
e **********************************
f *******
g ****
h *********
i ***********************
k **
l **********
m *************
n *******************
o *******************************
p *******
r *************************
s *********************
t *********************
u ***********
v **
w *
x *
y ****
Feel free to experiment by only counting the occurrences of digits, letters or punctuation (this can be easily done by modifying the int isprint(char c) condition with another one).

Character, Line and Word Counting in Text Files using ANSI C

To understand this tutorial you probably should know a little bit about how to operate with text files and external arguments. If you don't, read the following articles:

1. Counting characters
/* Description:
 * Counts the number of characters existing in the file specified by @stream.
 * Parameters:
 * stream - a pointer to a text file
 * Returns:
 *  The number of characters in the file.
 * Postconditions:
 *  The file pointer will be positioned at the end of the stream.
 */
long CountCharacters(FILE* stream)
{
    long counter = 0L;
    char c = 0x01;
    /*Counts until the cursor reaches EOF*/
    while(c!=EOF)
    {
        c = fgetc(stream);
        counter++;
    }
    return counter;
}
The function will return a long variable in order to avoid possible counter overflows caused by large files. The char variable c is initialized to an arbitrary variable so I could use a while loop instead of a do/while loop. The loop will be exited when the file cursor will reach the EOF (End of File) character.

2. Counting Lines
/* Description:
 * Counts the number of lines existing in the file specified by @stream.
 * Parameters:
 * stream - a pointer to a text file
 * Returns:
 *  The number of lines in the file.
 * Postconditions:
 *  The file pointer will be positioned at the end of the stream.
 */
long CountLines(FILE* stream)
{
 /*The counter is initialized to 1 because it will not count the first
  line*/
    long counter = 1L;
    char c = 0x01;
    /*Counts until the cursor reaches EOF*/
    while(c!=EOF)
    {
        c = fgetc(stream);
        /*Checks if it encounters and a newline character*/
        if(c=='\n')
        {
            counter++;
        }
    }
    return counter;
}
The same rules apply as above with the exception that the counter starts at 1 in order to include the first line. Also, the counter is only incremented when an '\n' (newline) character is encountered.

3.Counting Words
/* Description:
 * Counts the number of words existing in the file specified by @stream.
 * Parameters:
 * stream - a pointer to a text file
 * Returns:
 *  The number of words in the file.
 * Preconditions:
 *  We assume that after every punctuation mark and word delimiter there is a
 *  whitespace character.
 * Postconditions:
 *  The file pointer will be positioned at the end of the stream.
 */
long CountWords(FILE* stream)
{
    long counter = 0L;
    char c = 0x01;
    bool isInsideWord = true;
    while(c!=EOF)
    {
        c = fgetc(stream);
        if(isInsideWord==true)
        {
         counter++;
         isInsideWord = false;
        }
        else if(isspace(c))
        {
         isInsideWord = true;
        }
    }
    return counter;
}
We shall use the boolean variable isInsideWord in order to memorize the state of the file pointer. If a whitespace character was read, the state of isInsideWord will be toggled (will signify either the start or the end of a word).

I assumed that the text is written correctly and there no construction such as "There is no space after this point.Cool!". I tested the function with various text files and it gives results close to the word counter provided by LibreOffice.

4.Example
#include <stdio.h>
#include <stdbool.h>
#include <ctype.h>

#define NR_ARGS        2
#define FILE_ARG_INDEX 1

#include<stdio.h>

long CountCharacters(FILE* stream);
long CountLines(FILE* stream);
long CountWords(FILE* stream);

/*
 * Description:
 *  The program prints the number of characters, lines and words existing
 *  in a text file. It should be called like:
 *   Counter file.txt
 */
int main(int argc, char** argv)
{
 FILE* stream = NULL;
 long characters = 0UL, lines = 0UL, words = 0UL;
 if(argc==NR_ARGS)
 {
     stream = fopen(argv[FILE_ARG_INDEX],"rt");
     if(stream!=NULL)
     {
         characters = CountCharacters(stream);
         /*The function positions the cursor at the end of the
          stream. In order to count correctly the cursor should
          positioned at the start of the stream*/
         rewind(stream);
         lines = CountLines(stream);
         /*The function positions the cursor at the end of the
          stream. In order to count correctly the cursor should
          positioned at the start of the stream*/
         rewind(stream);
         words = CountWords(stream);
         /*The function positions the cursor at the end of the
          stream. In order to count correctly the cursor should
          positioned at the start of the stream*/
         rewind(stream);
         printf("Characters: %ld\n"
                "Lines     : %ld\n"
                "Words     : %ld\n",
                characters,lines,words);
         fclose(stream);
     }
     else
     {
         perror("Could not open file");
     }
 }
 else
 {
     perror("Incorrect number of arguments");
 }
 return 0;;
}


External Arguments in ANSI C

The easiest way to send data to your program is to use external arguments. An external argument is a string that can be processed by your program.

Let us suppose that we have a console program called ArgPrinter. To execute the program and send external arguments you will need to open a console\terminal and type something like this:
ArgPrinter arg1 arg2 arg3 arg4
Arguments can also be sent by using other methods, but those are system specific (Example: When you create a shortcut in windows you can go to the shortcut's properties and put the arguments there).

If you want to access the arguments who were sent, your main function should have this prototype:
int main(int argc, char**argv)
argc is a integer value who represents the number of arguments who were sent.
argv is a array of strings containing each of the sent arguments plus the path to your executable file.

TIPYes, this means that if you send 4 arguments argc will be equal to 5 because of the path argument.

Now let's take an example implementation of the ArgPrinter program:
#include<stdio.h>;

int main(int argc, char** argv)
{
   int i;
   int returnValue = 0;
   if(argc>0)
   {
      printf("Number of arguments: %d\n", argc);
      for(i = 0; i<argc; i++)
      {
         printf("%s\n",argv[i]);
      }
   }
   else
   {
      returnValue = -1;
   }
   return returnValue;
}
/*Output
Number of arguments: 5
/home/dystopiancode/Ubuntu One/Eclipse/ArgPrinter/ArgPrinter
Arg1
Arg2
Arg3
Arg4
 */
The program above will print the number of arguments and after that it will print the value of each argument.

The value that your program returns can signify the execution status of the program. Usually programs return 0 if they were executed successfully. This feature is useful if you call a C program from a shell script or something similar.

Question: Is your C program called directly from console using the external arguments or it is called by another program?

References:
http://crasseux.com/books/ctutorial/argc-and-argv.html

Friday, July 20, 2012

Simple File Copy Algorithm in ANSI C

The simplest implementation for creating a copy of a file is to copy the original file character by character into the new file.

Against all my expectations, I managed to create a copy of a 365 Mb file in under 40 seconds on my on my modest laptop running Ubuntu 12.04.

The program takes as arguments the path to the source file (first argument) and the path to the destination file (second argument).
#include<stdio.h>

/*The number of arguments that the program takes*/
#define ARG_NUMBER            3
/*The argument index for the source file path*/
#define SOURCE_ARG_INDEX      1
/*The argument index for the destination file path*/
#define DESTINATION_ARG_INDEX 2

/*Description:
 * The program does copies a file to another.It receives as arguments
 * the path to the source file and the path to the destination file.
 */
int main(int argc, char** argv)
{
    FILE *sourceStream = NULL, *destinationStream = NULL;
    char c;
    /*Verifies if the program received a correct number of arguments*/
    if (argc == ARG_NUMBER)
    {
        /*Opens a stream to the source file*/
        sourceStream = fopen(argv[SOURCE_ARG_INDEX], "rt");
        /*Checks if the stream was successfully established*/
        if (sourceStream != NULL)
        {
            /*Opens a stream to the destination file*/
            destinationStream = fopen(argv[DESTINATION_ARG_INDEX], "wt");
            /*Checks if the stream was successfully established*/
            if (destinationStream != NULL)
            {
                /*Copies the source file into the destionation file*/
                while (feof(sourceStream) == 0)
                {
                    c = fgetc(sourceStream);
                    fputc(c, destinationStream);
                }
                /*Closes the streams*/
                fclose(sourceStream);
                fclose(destinationStream);
            }
            else
            {
                perror("Could not open destination file.");
            }
        }
        else
        {
            perror("Could not open source file.");
        }
    }
    else
    {
        perror("Incorrect number of arguments. You should provide the "
                "path to the sourceStream file and the destinationStream file");
    }
    return 0;
}

Binary File I/O in ANSI C

The ANSI C library stdio.h provides functions for reading and writing binary files. If you are not familiar with stream operations in ANSI C you should read this first.

size_t fread(void *array, size_t size, size_t count, FILE *stream)
Description:
The function reads from the stream into the array count objects of the size size.
Returns:
The function returns the number of read objects. This number may be smaller than the requested number of objects.

size_t fwrite(const void *array, size_t size, size_t count, FILE *stream)
Description:
The function writes to the stream count objects of the array having the size size.
Returns:
The function returns the number of written objects. If the return value is smaller than count, then an error happened.

Example
#include<stdio.h>

#define MAX_NAME_SIZE    80
#define MAX_EMAIL_SIZE   60
#define MAX_PERSONS      10

typedef struct
{
   char name[MAX_NAME_SIZE];
   int age;
   double salary;
   char email[MAX_EMAIL_SIZE];
}Person;

int main(void)
{
   Person persons[MAX_PERSONS] =
   {
       {"Clark Kent", 30, 3250.5, "superman@krypton.com"},
       {"Peter Griffin", 40, 1250.0, "birdmaster@familyguy.com"},
       {"Stewie Griffin", 3, 50000.0, "world_domination@familyguy.com"},
       {"Eric Cartman", 12, 50000.0, "autoritah@southpark.com"},
       {"Zapp Brannigan", 30, 200.5, "stargeneral@futurama.com"},
   };
   Person pCopy[MAX_PERSONS];
   FILE* f = NULL;
   int i;
   /*Opens the stream in <writing binary> mode*/
   f = fopen("persons.bin","wb");
   if(f!=NULL)
   {
      /*Writes 5 Person objects into the binary file*/
      fwrite(&persons, sizeof(Person), 5, f);
      /*Closes the stream*/
      fclose(f);
      /*Reopens the stream in <read binary> mode*/
      f = fopen("persons.bin","rb");
      if(f!=NULL)
      {
         /*Reads 5 Person objects from the binary file into another vector*/
         fread(&pCopy,sizeof(Person),5,f);
         /*Prints the results*/
         for(i = 0; i<5; i++)
         {
            printf("%s %d %f %s\n",pCopy[i].name,pCopy[i].age,
                                   pCopy[i].salary,pCopy[i].email);
         }
         /*Closes the stream*/
         fclose(f);
      }
   }
   return 0;
}

File Positioning Functions in ANSI C

The ANSI library stdio.h provides 5 functions that can be used for file positioning:

int fseek(FILE *stream, long offset, int origin)
Description:
The function sets the current position for stream. A subsequent read or write will access data at the beginning of the new position. The functions takes as parameters a pointer to the accessed file (stream) and two other parameters which will determine the new position (offset and origin). 

The parameter origin represents the reference which will be used for the new position. The parameter may be SEEK_SET (the beginning of the file), SEEK_CUR (current position) or SEEK_END (the end of the file).

The parameter offset represents the offset from origin in bytes. 
Returns:
The function returns a non-zero value when it encountered an error. Otherwise it returns 0.

long ftell(FILE *stream)
Description:
The function returns the current cursor position for stream.
Returns:
The current cursor position in the stream if the operation was successful. Otherwise it returns -1.

void rewind(FILE *stream)
Description:
Puts the file positioning cursor at the start of the file.

int fgetpos(FILE *stream, fpos_t *position)
Description:
The function records in position the current position in stream so it can be used later by int fsetpos(FILE *stream, const fpos_t *ptr).
Returns:
The function returns 0 if it the operation was successful. Otherwise it returns a non-zero value.

int fsetpos(FILE *stream, const fpos_t *position)
Description:
The function positions the stream according to the position. The position is obtained by calling the function int fgetpos(FILE *stream, fpos_t *position).
Returns:
The function returns 0 if it the operation was successful. Otherwise it returns a non-zero value.

Example:
Let us suppose that we have a file "file.txt" with the following content:
123456789
987654321
#include<stdio.h>
/*Legend:
 * The stream cursor will be represented with
 * the letter C.
 */
int main(void)
{
   FILE* f = fopen("file.txt","rt");
   char c;
   long result;
   int i;
   fpos_t position;

   /*Positions the cursor at:
      123C456789\n
      987654321
      The next read character will be 4*/
   fseek(f,3,SEEK_SET);
   c = fgetc(f);
   putchar(c);

   /*Positions the cursor at:
      12C3456789\n
      987654321
      The next read character will be 3*/
   fseek(f,-2,SEEK_CUR);
   c = fgetc(f);
   putchar(c);

   /*Positions the cursor at:
      123456789\n
      98765432C1
      The next read character will be 1*/
   fseek(f,-1,SEEK_END);
   c = fgetc(f);
   putchar(c);

   /*The cursor is now positioned at:
      123456789\n
      987654321C
      The indicated position will be 19 (the end of the file)*/
   result = ftell(f);
   printf("\nCurrent position: %ld",result);

   /*Puts the cursor at the begining of the file*/
   rewind(f);
   /*The cursor is now positioned at:
      C123456789\n
      987654321
      The indicated position will be 19 (the end of the file)*/
   result = ftell(f);
   printf("\nCurren position: %ld\n",result);

   /*Moves the cursor 3 positions (bytes)*/
   for(i = 0; i<3; i++)
   {
      c = fgetc(f);
   }
   /*The cursor is now positioned at:
       123C456789\n
       987654321
   */
   fgetpos(f,&position);
   /*Moves the cursor another 3 positions (bytes)*/
   for(i = 0; i<3; i++)
   {
      c = fgetc(f);
   }
   /*The cursor is now positioned at:
       123456C789\n
       987654321
       The last read character was 6
   */
   putchar(c);
   /*Positions the cursor at the memorized position*/
   fsetpos(f,&position);
   /*The cursor is now positioned at:
        123C456789\n
        987654321
        The next read character will be 4.*/
   c = fgetc(f);
   putchar(c);

   fclose(f);
   return 0;
}

Reading and Writing Structures from/to Text Files in ANSI C

The only way to keep a program's data after exiting the program is to write the data in a file. By writing the program's data into a file you will be able to use it next time you start the program.

Let us suppose that we have a structure Person with the following attributes:
-Name (string) (the string can contain spaces)
-Age (integer)
-Salary (real)
-Email (string) (cannot contain spaces)

We shall consider a simple program that writes into a text file a struct vector of the Person type and then loads from the same text file the data into a new struct vector of the Person type.
#include<stdio.h>
#include<string.h>

/*The size of the buffer used for reading from the text file*/
#define BUFFER_SIZE   250
/*The maximum size of the name field in the Person struct*/
#define MAX_NAME_SIZE    80
/*The maximum size of the email field in the Person struct*/
#define MAX_EMAIL_SIZE   60
/*The maximum number of persons*/
#define MAX_PERSONS      10
/*The relative path to the file where you can find the text file*/
#define PATH_TO_TXT_FILE "persons.txt"
/*The character who will be used to replace the space character*/
#define SPACE_REPLACEMENT   '$'

typedef struct
{
   char name[MAX_NAME_SIZE];
   int age;
   double salary;
   char email[MAX_EMAIL_SIZE];
}Person;

/*
 * Description:
 *  Replaces all occurrences of @replacedChar with @replacementChar
 * Parameters:
 *  string - the string where the replacement process will take place
 *  replacedChar - the character who will be replaced
 *  replacementChar - the character who will replace the @replacedChar
 * Returns:
 *  Nothing
 */
void ReplaceChar(char* string, char replacedChar, char replacementChar)
{
   int size = strlen(string);
   int i;
   for(i=0; i<size;i++)
   {
      if(string[i]==replacedChar)
      {
         string[i] = replacementChar;
      }
   }
}
/*
 * Description:
 *  Outputs to stdout the content of the Person vector
 * Parameters:
 *  p - a pointer to the the Person vector
 *  numberOfPersons - how many elements the vector has
 * Returns:
 *  Nothing
 */
void Persons_Print(Person *p, int numberOfPersons)
{
   int i;
   for(i = 0; i<numberOfPersons; i++)
   {
      printf("%s %d %f %s\n",p[i].name,p[i].age,p[i].salary,p[i].email);
   }
}
/*
 * Description:
 *  Saves in the text file represented by @stream the content of the
 *  person vector.
 * Parameters:
 *  p - a pointer to the Person vector
 *  numberOfPersons - how many elements the vector has
 *  stream - a pointer to the text file
 */
void Persons_Save(Person *p, int numberOfPersons, FILE* stream)
{
   int i;
   for(i = 0; i<numberOfPersons; i++)
   {
      /*Replaces all spaces in the name string in order to be
        able to load a person's name */
      ReplaceChar(p[i].name,' ',SPACE_REPLACEMENT);

      /*Writes the structure into the text file*/
      fprintf(stream, "%s %d %f %s\n",p[i].name,p[i].age,
                                      p[i].salary,p[i].email);

      /*Brings the name string back to normal*/
      ReplaceChar(p[i].name,SPACE_REPLACEMENT,' ');
   }
}
/*
 * Description:
 *  Loads in the Person vector the contents of the text file
 * Parameters:
 *  p - a pointer to the Person vector
 *  numberOfPersons - how many elements the vector has
 *  stream - a pointer to the text file
 * Returns:
 *  Nothing
 */
void Persons_Load(Person *p, int *numberOfPersons, FILE* stream)
{
   char buffer[BUFFER_SIZE];
   char *result = NULL;
   while(feof(stream)==0)
   {
      /*Reads a line of the file*/
      result = fgets(buffer,BUFFER_SIZE,stream);
      /*Checks if the line reading was successful*/
      if(result!=NULL)
      {
         /*Interprets the line of the text and adds a new entry in the struct
           vector*/
         sscanf(buffer,"%s %d %f %s",p[(*numberOfPersons)].name,
                                     &p[(*numberOfPersons)].age,
                                     &p[(*numberOfPersons)].salary,
                                     p[(*numberOfPersons)].email);
         /*Replaces the SPACE_REPLACEMENT character with space*/
         ReplaceChar(p[(*numberOfPersons)].name,SPACE_REPLACEMENT,' ');
         /*Increments the Person counter*/
         (*numberOfPersons)++;
      }
    }
}

int main(void)
{
   Person persons[MAX_PERSONS] =
   {
      {"Clark Kent", 30, 3250.5, "superman@krypton.com"},
      {"Peter Griffin", 40, 1250.0, "birdmaster@familyguy.com"},
      {"Stewie Griffin", 3, 50000.0, "world_domination@familyguy.com"},
      {"Eric Cartman", 12, 50000.0, "autoritah@southpark.com"},
      {"Zapp Brannigan", 30, 200.5, "stargeneral@futurama.com"},
   };
   Person personsCopy[MAX_PERSONS];
   int numberOfPersons = 0;
   FILE* stream = NULL;

   /*Opens the stream into <write text> mode*/
   stream = fopen(PATH_TO_TXT_FILE,"wt");
   if(stream!=NULL)
   {
      /*Save the struct vector into the text file*/
      Persons_Save(&persons,5,stream);
      /*Closes the stream*/
      fclose(stream);
      /*Reopens the stream into <read text> mode*/
      stream = fopen(PATH_TO_TXT_FILE,"rt");
      if(stream!=NULL)
      {
         /*Loads the text file into another struct vector*/
         Persons_Load(&personsCopy, &numberOfPersons, stream);
         /*Closes the stream*/
         fclose(stream);
         /*Prints the results*/
         Persons_Print(&personsCopy,numberOfPersons);
      }
   }
   return 0;
}
A problem arises when you want to read from the text file a string that contains spaces because int sscanf ( const char * str, const char * format, ...) will not interpret it as a single string.

To solve this problem you can replace all space characters with a uncommon character for that string(in this example, I used '$' because is very uncommon when it comes to names).

Saturday, July 14, 2012

Text File I/O Operations in ANSI C

The ANSI C library stdio.h provides support for working with files and streams. To use a file, a pointer is necessary to make the connection between the physical file and the C I/O system.

1.Reading from a text file
int fgetc(FILE *stream)
Description
The function returns the next character of the stream as an unsigned char (converted to an int). It takes as parameters a pointer to the stream.
Returns
The read character from the stream or EOF if it encounters the end of file or if an error occurs.
Example
#include<stdio.h>

int main(void)
{
   FILE* f = NULL;
   char c;
   /*Opens the stream*/
   f = fopen("file.txt", "rt");
   if (f != NULL)
   {
      /*Reads the file character by character and
      prints the results*/
      do
      {
         c = fgetc(f);
         if (c != EOF)
         {
            putchar(c);
         }
      } while (c != EOF);
      fclose(f);
   }
   else
   {
      puts("Could not open file");
   }
   return 0;
}
The program above reads a text file and prints its content character by character.

char* fgets(char* string, int n,  FILE *stream)
Description
The function reads at most n-1 characters into the character array string, stopping if it encounters a newline character (which will be included in the array as well). A string terminator will be added to the array in the end ('\0').
Returns
The function returns a pointer to the character array if the operation was successful or NULL if an error was encountered.
Example
#include<stdio.h>

int main(void)
{
    const int BUFFER_SIZE = 200;
    FILE* f = NULL;
    char buffer[BUFFER_SIZE];
    /*Opens the stream*/
    f = fopen("file.txt","rt");
    if(f!=NULL)
    {
       /*Reads the file line by line until EOF is encountered*/
       while(feof(f)==0)
       {
          /*Reads a line*/
          fgets(buffer,BUFFER_SIZE,f);
          /*Prints the line*/
          puts(buffer);
       }
       fclose(f);
    }
    else
    {
       puts("Could not open file");
    }
    return 0;
}
int fscanf(FILE *stream, const char *format, ...)
Description
The function reads from the stream formatted data and assigns values to the subsequent arguments. It is very similar to int scanf(const char *format, ...) with the exception that you can specify the used stream. If you want to learn more, check the articles on console I/O and formatted strings.
Returns
The function returns the number of items who were converted and assigned. If it encounters the end of the file or if an error happens it will return EOF.
Example
We shall consider a file containing only integers which are separated by a space character:
#include<stdio.h>

int main(void)
{
   FILE* f = NULL;
   int number = 0;
   int status = 0;
   /*Opens the stream*/
   f = fopen("file.txt","rt");
   if(f!=NULL)
   {
      /*Reads every integer from the file and prints it*/
      while(feof(f)==0)
      {
         /*Tries to read an integer*/
         status = fscanf(f,"%d",&number);
         if(status!=EOF)
         {
            printf("%d ",number);
         }
         else
         {
            puts("Non-integer found");
         }
      }  
      fclose(f);
   }
   else
   {
      puts("Could not open file");
   }
   return 0;
}
2.Writing to a Text File
int fputc(int c, FILE *stream)

Description
The function writes the character c (represented as an integer) to the stream. It takes as parameters the character (c) and a pointer to the stream (stream).
Returns
The function returns the written character if the operation was successful. Otherwise it returns EOF.
Example
We shall consider a file containing only integers which are separated by a space character:
#include<stdio.h>
#include<string.h>

int main(void)
{
   FILE* f = NULL;
   int i;
   char sentence[] = "The bird is the word";
   int size, status;
   /*Opens the stream*/
   f = fopen("file.txt","wt");
   if(f!=NULL)
   {
      /*Computes the length of the character array*/
      size = strlen(sentence);
      /*Writes the character array character by character*/
      for(i = 0; i<size; i++)
      {
         status = fputc(sentence[i],f);
         if(status == EOF)
         {
            puts("Error occurred while writing the file");
         }
      }  
   }
   else
   {
      puts("Could not open file");
   }
   return 0;
}
int fputs(const char *s, FILE *stream)
Description
The function writes the string s to the stream stream. The string doesn't need to co
Returns
The function returns a non-negative number if the operation was successful. Otherwise it returns EOF.
Example
#include<stdio.h>

int main(void)
{
   FILE* f = NULL;
   char sentence1[] = "The bird is the word\n";
   char sentence2[] = "Bird! Bird! Bird!\n";
   /*Opens the stream*/
   f = fopen("file.txt","wt");
   if(f!=NULL)
   {
      /*Writes the first sentence*/
      fputs(sentence1,f);
      /*Writes the second sentence*/
      fputs(sentence2,f);
   }
   else
   {
      puts("Could not open file");
   }
   return 0;
}

int fprintf(FILE *stream, const char *format, ...)
Description
The function writes to the stream formatted data.  It is very similar to int printf(const char *format, ...), with the exceptions that you can specify the used stream. If you want to learn more, check the articles on console I/O and formatted strings.
Returns
The function returns the number of written characters if the operation was successful. Otherwise it returns a negative number.
#include<stdio.h>

#define NAME_SIZE 60

typedef struct
{
   char name[NAME_SIZE];
   int age;
}Person;

int main(void)
{
   FILE* f = NULL;
   Person john = {"John Doe", 30};

   /*Opens the stream*/
   f = fopen("file.txt","wt");
   if(f!=NULL)
   {
      /*Writes formatted text*/
      fprintf(f,"Name: %s | Age: %d",john.name, john.age);
   }
   else
   {
      puts("Could not open file");
   }
   return 0;
}

Creating, Removing and Renaming Files in ANSI C

The ANSI C library stdio.h provides support for working with files and streams. To use a file, a pointer is necessary to make the connection between the physical file and the C I/O system.

1.Creating a File

To create you can use FILE* fopen(char* fileName, char* mode) using the mode "wt". This will create a new file file at the specified filename. If you are not familiar with this function, read this.

Example:
#include<stdio.h>

int main(void)
{
   const int numberOfFiles = 10;

   FILE* f = NULL;
   char filename[FILENAME_MAX];
   int i = 0;

   /*Tries to create 10 files*/
   for(i = 0; i<numberOfFiles; i++)
   {   
       /*Dynamically creates the filename*/
       sprintf(filename,"MyFile%d.fil",i);
       /*Creates the file*/
       f = fopen(filename, "wt");
       /*Checks if the file was created successfully*/
       if(f!=NULL)
       { 
           printf("%s created successfully\n",filename);
           /*Closes the current stream*/
           fclose(f);
       }
       else
       {
           printf("%s could not be created\n",filename);
       }
   }
   return 0;
}
The program above will create 10 files in the current working directory called "MyFile0fil", "MyFile1.fil", ..., "MyFile9.fil"

2.Renaming a File

To rename a file, you must the function int rename(const char *oldname, const char *newname). The function takes as parameters the name of the existing file (oldname) and the new name of the file (newname). The function will return 0 if the operation was successful and a non-zero number if it failed.

Example:
#include<stdio.h>

int main(void)
{
   int status = rename("MyFile0.fil", "MyNewFile0.lif");
   if(status == 0)
   {
      puts("New file successfully renamed");
   }
   else
   {
      puts("Rename operation failed");
   }
   return 0;
}
The program above will take the file "MyFile0.fil" created in the precedent example and it will rename it to "MyNewFile0.lif".

3.Removing a File

To remove a file, you must use the function int remove(const char *filename). The function takes as parameters a string containing the path of the file which will be removed. It returns 0 if the operation was successful or a non-zero number if an error happened along the way.

Example:
#include<stdio.h>

int main(void)
{  
   int status = remove("MyNewFile0.lif");
   if(status == 0)
   {
      puts("File successfully removed");
   }
   else
   {
      puts("Remove operation failed");
   }
   return 0;
}
The program above will remove the file "MyNewFile0.lif" created in the precedent example.

Opening and Closing Streams in ANSI C

The ANSI C library stdio.h provides support for working with files and streams. To use a file, a pointer is necessary to make the connection between the physical file and the C I/O system.

1.Opening a stream
FILE* fopen(char* fileName, char* mode)
Description:
The function opens a stream to the named file (specified by its file name - char* fileName) in a specified mode (specified by char* mode).

The available modes are:
"rt"\"rb" Open file for reading
"wt"\"wb" Create file for writing. If any previous content exist, it shall be discarded
"at"\"ab" Opens or creates a file for writing, starting at the end of the file
"r+t"\"r+b" Open a file for update
"w+t"\"w+b" Creates text file for update. If any previous content exist, it shall be discarded
"a+t"\"a+b" Opens or creates a file for writing, starting at the end of the file

Files can be opened in text mode (example: "rt" opens a file for reading in text mode while "rb" opens a file for reading in binary mode).

The update mode (example "w+t") permits reading and writing on the same file. You must use int fflush(FILE* stream) or a file-positioning function between a read and a write to move the cursor at the position you want.
Returns:
The function returns a stream (FILE*) to the file if the call is successful. Otherwise returns NULL.
Tips&Tricks:
-The number of characters in a file name is limited by the FILENAME_MAX macro.
-The number of opened streams (files) is limited by the FOPEN_MAX macro.

2.Closing a Stream
int fclose(FILE* stream).
Description:
The function flushes any unwritten data for the stream sent as parameter, discards any unread buffered input and frees any automatically allocated buffer. After that it closes the stream.
Returns
0 - if the stream was successfully closed
EOF - if an error happened while closing the stream.

3.Example
#include<stdio.h>

int main(void)
{
   /*Variables*/
   FILE* f = NULL;
   int status = 0;

   /*Opens the stream*/
   f = fopen("file1.txt","rt");
   /*Checks if the stream was opened correctly*/
   if(NULL!=f)
   {
      puts("File was opened successfully");
      /*Closes the file*/
      status = fclose(f);
      /*Checks if the stream was closed correctly*/
      if(0==status)
      {
          puts("File closed successfully");
      }
      else
      {
          puts("File could not be closed");
      }
   }  
   else
   {
      puts("Could not access file");
   }
   return 0;
}


Sunday, July 8, 2012

Palindrome Checking Algorithm in ANSI C

A palindrome is a number or a string who after being reversed is equal to its original form.

Example: mom, dad, 123321

1.Checking if a Number is a Palindrome
The algorithm for checking if a number is a palindrome is:
1)Create a copy of the number
2)Using that copy, build the reverse of the number
3)If the reverse of the number is equal to the original number, then the number is a palindrome

Here's an implementation of the number palindrome check:
/*
 * Description:
 *  Checks if a integer is a palindrome
 * Parameters:
 *  number - the integer who is going to be verified
 * Returns:
 *  true - the integer is a palindrome
 *  false - the integer is not a palindrome
 */
bool IntegerIsPalindrome(int number)
{
   int copy = number;
   int reverseNumber = 0;
   while(copy!=0)
   {
      reverseNumber= reverseNumber*10 + copy%10;
      copy/=10;
   }
   return (number==reverseNumber);
}
2.Checking if a String is a Palindrome
The algorithm for checking if a string is a palindrome is:
1)Assume that the string is a palindrome
2)Compare the first character with the last character, the second character and character before the last character and so on until you hit the middle of the string.
3)If any of the pairs compared are not equal, the string is not a palindrome.
/*
 * Description:
 *  Checks if a string is a palindrome
 * Parameters:
 *  string - the string who is going to be verified
 * Returns:
 *  true - the string is a palindrome
 *  false - the string is not a palindrome
 */
bool StringIsPalindrome(const char* string)
{
   int length = strlen(string);
   int i;
   bool isPalindrome = true;
   for(i = 0; i<(length/2); i++)
   {
      if(string[i]!=string[length-i-1])
      {
         isPalindrome = false;
         break;
      }
   }
   return isPalindrome;
}
3.Example
/*Example*/
int main(void)
{
   printf("%d %d %d\n",StringIsPalindrome("ABAABA"),
           StringIsPalindrome("MoM"),
           StringIsPalindrome("Camus"));
   printf("%d %d %d\n",IntegerIsPalindrome(-121),
           IntegerIsPalindrome(121),
           IntegerIsPalindrome(334));
   return 0;
}
/*Output
1 1 0
1 1 0
 */
Note:
Do not forget to include the headers: <stdbool.h> is needed for both functions and <string.h> is needed for the string palindrome checking function.

Saturday, July 7, 2012

Implementing a Tic Tac Toe Rule Engine in Java

In order to implement a Tic Tac Toe Rule Engine we shall define the following entities:
-Model, which shall be used as a base for the rule engine
-ModelOptions, which shall be used for containing the options for creating a model
-Direction, which shall be used to simplify the winning condition check.
-GameStatus, which shall be used as the return result after a winning condition check.
-MoveStatus, which shall be used as the return result after a player makes a move.
-Player, which shall be used for containing the information about the players.

We shall consider that the Tic Tac Toe game takes place on a n x m board having 2 or more players, where n can be equal m.

For the model we shall define the following the attributes:
-Number of players
-The width and height of the game table
-The number of symbols needed for a player to win

The model must allow the following operations:
-Put symbol, which shall allow a player to insert his symbol at a certain position on the table if that position was not occupied by another player.
-Winning condition check, which needs to verify the table and signal if a player managed to have enough symbols placed in a line/diagonal configuration. 

The implementation for the Model is:
package tictactoe.ruleengine;

import java.awt.Point;

/**
 *
 * @author bogdan
 */
public class Model 
{
    /**
     * A reference to the options used for creating the model
     */
    private ModelOptions options;
    /**
     * The game table
     */
    private int[][] table;
    
    /**
     * Creates new Model object 
     * @param options The options used for creating the model
     */
    public Model(ModelOptions options) 
    {
        this.options = options;
        this.table = new int[options.getHeight()][options.getWidth()];
        clearTable();
    }
    
    /**
     * Initializes the game table with the default values 
     */
    private void clearTable()
    {
        int i,j;
        int height = options.getHeight();
        int width = options.getWidth();
        for(i = 0; i<height; i++)
        {
            for(j = 0; j<width; j++)
            {
                table[i][j] = Player.NO_PLAYER;
            }
        }
    }
    /**
     * Checks if the coordinates are within the game table bounds
     * @param x The X coordinate of the point
     * @param y The Y coordinate of the point
     * @return True if the point is within bounds, false otherwise
     */
    private boolean isWithinBounds(int x, int y)
    {
        return ((x>=0) && (x<options.getHeight()) 
                && (y>=0) && (y<options.getWidth()));
    }
    /**
     * Verifies if P(i,j) is situated on a line or diagonal with enough symbols
     * in order for the player to win.
     * @param i The X coordinate of the point
     * @param j The Y coordinate of the point
     * @param symbol The type of symbol who is counted
     * @return True - if a player won
     *         False - No one one.
     */
    private boolean checkSquare(int i, int j, int symbol)
    {
        int k;
        int counter;
        int x, y;
        boolean hasWon = false;
        Direction dir;
        int symbolsToWin = options.getSymbolsToWin();
        //Checks in every direction if there are enough symbols in order
        //for a player to win
        for(k = Direction.FIRST_SYMBOL; k< Direction.LAST_SYMBOL; k++)
        {
            counter = 1;
            x = i;
            y = j;
            while(counter!=symbolsToWin)
            {
                dir = Direction.getDirection(k);
                x += dir.getXOffset();
                y += dir.getYOffset();
                if((isWithinBounds(x, y)==true)&& (table[x][y]==symbol) &&
                    (counter<symbolsToWin))
                {
                    counter++;
                }
                else
                {
                    break;
                }
            }
            if(counter==symbolsToWin)
            {
                hasWon = true;
                break;
            }
        }
        return hasWon;
    }
    /**
     * Verifies each square to see if any player won
     * @return 
     * GameStatus.GAME_OVER - The game is over, no one won (if there no more
     * free squares)
     * GameStatus.GAME_PENDING - No one won yet
     * Otherwise returns a GameStatus object initialized with the symbol of
     * the winning player.
     */
    public GameStatus checkWinningConditions()
    {
        int i,j;
        int result = Player.NO_CHANGE;
        boolean hasWon;
        int emptySymbolCounter = 0;
        int height = options.getHeight();
        int width = options.getWidth();
        //Checks for every square if winning conditions are achieved
        for(i=0; (i<height && result==-1); i++)
        {
            for(j=0; (j<width && result==-1); j++)
            {
               if(table[i][j]!=Player.NO_PLAYER)
               {
                   hasWon = checkSquare(i,j,table[i][j]);
                   if(hasWon==true)
                   {
                       result = table[i][j];
                   }
               }
               else
               {
                   emptySymbolCounter++;
               }
            }
        }
        if((result==-1) && (emptySymbolCounter==0))
        {
            result = Player.NO_PLAYER;
        }
        return new GameStatus(result);
    }
    /**
     * Inserts a symbol on the game table
     * @param x The X coordinate at which the symbol is inserted
     * @param y The Y coordinate at which the symbol is inserted
     * @param symbol The value of the symbol who will be inserted
     * @return 
     * MoveStatus.IsValid - if the insertion is valid
     * MoveStatus.AlreadyOccupied - 
     */
    public MoveStatus putSymbol(int x, int y, int symbol)
    {
        int i;
        boolean found = false;
        MoveStatus status =MoveStatus.None;
        int[] playerSymbols = Player.getAllPlayerSymbols();
        for(i = 0; i< playerSymbols.length; i++)
        {
            if(symbol == playerSymbols[i])
            {
                if(isWithinBounds(x, y)==true)
                {
                    if(table[x][y]==Player.NO_PLAYER)
                    {
                        table[x][y] = symbol;
                        status = MoveStatus.Valid;
                    }
                    else
                    {
                        status = MoveStatus.AlreadyOccupied;
                    }
                }
                else
                {
                    status = MoveStatus.OutOfBounds;
                }
                found=true;
                break;
            }
        }
        if(found==false)
        {
            status = MoveStatus.UnknownSymbol;
        }  
        return status;
    }
    
    /**
     * Returns a string representation of the table
     * @return A string representation of the table
     */
    @Override
    public String toString()
    {
        int i,j;
        int height = options.getHeight();
        int width = options.getWidth();
        StringBuilder builder = new StringBuilder();
        for(i = 0; i< height; i++)
        {
            for(j = 0; j<width; j++)
            {
                builder.append(table[i][j]);
                builder.append(" ");
            }
            builder.append("\n");
        }
        return builder.toString();
    }
    
    /**
     * Returns the symbol at the point P(i,j)
     * @param i The X coordinate of the point
     * @param j The Y coordinate of the point
     * @return The symbol at the point P(i,j) if i and j are within the table
     * bounds. Otherwise returns Integer.MAX_VALUE;
     */
    public int getSymbolAt(int i, int j)
               throws IllegalArgumentException
    {
        if(isWithinBounds(i,j)==true)
        {
            return table[i][j];
        }
        else
        {
            return Integer.MAX_VALUE;
        }
    }
}
The implementation for the ModelOptions is:
package tictactoe;

import java.util.ArrayList;

/**
 *
 * @author dystopiancode
 */
public class ModelOptions
{
    /**
     * A reference to an ArrayList containing the players.
     */
    private ArrayList<Player> players;
    /**
     * The width of the game table
     */
    private int width;
    /**
     * The height of the game table
     */
    private int height;
    /**
     * The number of symbols that need to be in a line or a row in order for
     * a player to win
     */
    private int symbolsToWin;

    /**
     * Creates a new ModelOptions object
     * @param players A reference to an ArrayList containing the players.
     * @param height The height of the game table
     * @param width The width of the game table
     * @param symbolsToWin The number of symbols that need to be in a line or 
     * a row in order for a player to win
     * @throws IllegalArgumentException  
     */
    public ModelOptions(ArrayList<Player> players, 
                        int height,
                        int width,
                        int symbolsToWin)
           throws IllegalArgumentException
    {
        if((symbolsToWin<=height) && (symbolsToWin<=width))
        {
            this.players = players;
            this.width = width;
            this.height = height;
            this.symbolsToWin = symbolsToWin;
        }
        else
        {
            throw new IllegalArgumentException("Size of table too small");
        }
    }
    /**
     * Returns a reference to an ArrayList containing the players
     * @return A reference to the players Arraylist
     */
    public ArrayList<Player> getPlayers()
    {
        return players;
    }
    /**
     * Returns the number of symbols needed for winning
     * @return The number of symbols needed for winning
     */
    public int getSymbolToWin()
    {
        return symbolsToWin;
    }
    /**
     * Returns the height of the game table
     * @return The height of the game table
     */
    public int getHeight()
    {
        return height;
    }
    /**
     * Returns the width of the game table
     * @return The width of the game table
     */
    public int getWidth()
    {
        return width;
    }  
}
The implementation for Direction is:
package tictactoe;

/**
 * @author dystopiancode
 */
public enum Direction
{
    NONE(0,0,0),
    NORTH(1,-1,0),
    SOUTH(2,1,0),
    EAST(3,0,-1),
    WEST(4,0,1),
    NORTH_EAST(5,-1,-1),
    NORTH_WEST(6,-1,1),
    SOUTH_EAST(7,1,-1),
    SOUTH_WEST(8,1,1);
    
    /**
     * The symbol for no direction
     */
    public static final int NONE_SYMBOL = 0;
    /**
     * The symbol for the first direction
     */
    public static final int FIRST_SYMBOL = 1;
    /**
     * The symbol for the last direction
     */
    public static final int LAST_SYMBOL = 8;
    
    /**
     * The symbol used for representing a direction
     */
    private int _symbol;
    /**
     * The x axis offset introduced by changing the location according to
     * the direction.
     */
    private int _xOffset;
    /**
     * The y axis offset introduced by changing the location according to
     * the direction.
     */
    private int _yOffset;
 
    /**
     * Creates a Direction object
     * @param symbol The symbol used for representing the direction
     * @param xOffset The x axis offset introduced by changing the location 
     * according to the direction.
     * @param yOffset The y axis offset introduced by changing the location 
     * according to the direction.
     */
    private Direction(int symbol, int xOffset, int yOffset)
    {
        _symbol = symbol;
        _xOffset = xOffset;
        _yOffset = yOffset;
    }
    
    /**
     * Return the x axis offset
     * @return The x axis offset
     */
    public int getXOffset()
    {
        return _xOffset;
    }
    /**
     * Return the y axis offset
     * @return The y axis offset
     */
    public int getYOffset()
    {
        return _yOffset;
    }
    /**
     * Return the symbol of the direction
     * @return The symbol
     */
    public int getSymbol()
    {
        return _symbol;
    }
    
    /**
     * Creates a direction according to a symbol
     * @param symbol The symbol used for creating the direction
     * @return The direction who has the indicated symbol
     */
    public static Direction getDirection(int symbol)
    {
        switch(symbol)
        {
            case 1:
                return Direction.NORTH;
            case 2:
                return Direction.SOUTH;
            case 3:
                return Direction.EAST;
            case 4:
                return Direction.WEST;
            case 5:
                return Direction.NORTH_EAST;
            case 6:
                return Direction.NORTH_WEST;
            case 7:
                return Direction.SOUTH_EAST;
            case 8:
                return Direction.SOUTH_WEST;
            default:
                return Direction.NONE;
        }
    }
}
The implementation for GameStatus is:
package tictactoe;

/**
 *
 * @author dystopiancode
 */
public class GameStatus
{   
    /**
     * The game is over, no on won
     */
    public static GameStatus GAME_OVER = new GameStatus(Player.NO_PLAYER);
    /**
     * The game is still running
     */
    public static GameStatus GAME_PENDING = new GameStatus(Player.NO_CHANGE);
    
    /**
     * The symbol encapsulated by the GameStatus
     */
    private int symbol;

    /**
     * Creates a new GameStatus object
     * @param symbol The symbol representing the status (usually the symbol
     * of the player who won the game).
     */
    public GameStatus(int symbol)
    {
        this.symbol = symbol;
    }
    /**
     * Returns the symbol of the winning player, if any.
     * @return The symbol of the winning player
     */
    public int getSymbol()
    {
        return symbol;
    }
    /**
     * Verifies if the GameStatus objects are equal
     * @param status The object who is going to be compared with the current
     * object
     * @return True if the object are equal, false otherwise 
     */
    public boolean equals(GameStatus status)
    {
        return (symbol==status.symbol);
    }
    
}
The implementation for MoveStatus is:
package tictactoe;

/**
 *
 * @author dystopiancode
 */
public enum MoveStatus
{
    None(0),
    Valid(1),
    AlreadyOccupied(2),
    OutOfBounds(3),
    UnknownSymbol(4);
    
    /**
     * A integer representing the code of a status resulted after a player
     * made a move.
     */
    private int statusCode;
    
    /**
     * Private constructor for the enumeration
     * @param statusCode The code of the current status
     */
    private MoveStatus(int statusCode)
    {
        this.statusCode = statusCode;
    }
    
    /**
     * Checks if two MoveStatus objects are equal
     * @param status The object to which the current object will be compared
     * @return True if the objects are equal, otherwise false
     */
    public boolean equals(MoveStatus status)
    {
        return (this.statusCode == status.statusCode);
    }
}
The implementation for Player is:
package tictactoe;

/**
 *
 * @author dystopiancode
 */
public class Player 
{
    /**
     * 
     */
    public static final int NO_CHANGE = -1;
    public static final int NO_PLAYER = 0;
    
    /**
     * Static counter-like variable used for assigning a symbol for each new
     * player.
     */
    private static int symbolCounter = 1;
    
    /**
     * The name of the player
     */
    private String name;
    /**
     * The symbol used for representing a a player on the game table
     */
    private int symbol;

    /**
     * Createa a new Player object
     * @param name The name of the player
     */
    public Player(String name) 
    {
        this.name = name;
        symbol = symbolCounter;
        symbolCounter++;
    }
    /**
     * Returns the player's symbol
     * @return The Player's symbol
     */
    public int getSymbol()
    {
        return symbol;
    }
    /**
     * Returns the player's name
     * @return The player's name
     */
    public String getName()
    {
        return name;
    }
    /**
     * Creates a vector containing all the symbols used by the existing players
     * @return A vector containing all the symbols used by the existing players
     */  
    public static int[] getAllPlayerSymbols()
    {
        int[] symbols = new int[symbolCounter];
        for(int i = 0; i<symbolCounter; i++)
        {
            symbols[i] = i;
        }
        return symbols;
    }
}

Sunday, July 1, 2012

The GDS ArraySet Data Structure

We define the ArraySet data structure as:
typedef struct
{
   /*The allocated size for the Element array elements*/
   int capacity;  
   /*How many elements are actually in the array*/
   int size;
   /*The vector containing the elements*/
   Element *elements;
}ArraySet;
The following operations will be implemented:
Operation Description
Create Creates a new ArraySet data structure
Resize Modifies the capacity of the ArraySet in order to save memory
Destroy Destroys an ArraySet in order to free memory
Belongs Checking if an object already exists in the set
Add object Add a new Element to the ArraySet. Before adding it should check that the element doesn't already exists in the set (sets do not allow duplicates).
Remove object Removes an Element from the ArraySet
Remove all Removes an object from the ArraySet
Copy Creates a hard copy of the ArraySet
Equals Check if two sets contain the same elements
Is subset Checks if all elements in the first set also belong in the secondary set
Reunion Creates a new ArraySet containing all unique objects from two sets
Intersection Creates a new ArraySet containing the objects that belong to two sets
Difference Creates a new ArraySet containing the objects that belong the the first set, but do not belong to the second set

The functions who are implementing these operations are:
/*
 * Description:
 *  Creates an ArraySet data structure
 * Parameters:
 *  set - a pointer to the set who is going to be created
 * Returns:
 *  A pointer to the initialized set OR
 *  NULL if there is not enough memory
 */
ArraySet* ArraySet_Create(int capacity);
/*
 * Description:
 *  Resizes an ArraySet data structure
 * Parameters:
 *  capacity - the new capacity of the set
 * Returns:
 *  A pointer to the set OR
 *  NULL if there is no memory left or the newCapacity is
 *  smaller than the size of the array.
 */
ArraySet* ArraySet_Resize(ArraySet* set, int newCapacity);
/*
 * Description:
 *  Destroys an ArraySet structure
 * Parameters:
 *  set - a pointer to the set who is going to be created
 * Returns:
 *  NULL
 */
ArraySet* ArraySet_Destroy(ArraySet* set);
/*
 * Description:
 *  Verifies if @object belongs in @set
 * Parameters:
 *  object - a pointer to a object
 *  set - a pointer to a set
 * Returns:
 *  True - if the object exists in the set
 *  False - if the object does not exist in the set
 */
bool ArraySet_Belongs(const Object* object, const ArraySet* set);
/*
 * Description:
 *  Adds an object to a set
 * Parameters:
 *  set - a pointer to the current set
 *  object - a pointer to an object
 * Returns:
 *  A pointer to the current set OR
 *  NULL if the object already exists in the set or
 *    if adding the object would cause a buffer overflow
 */
ArraySet* ArraySet_AddObject(ArraySet* set, const Object* object);
/*
 * Description:
 *  Removes an object from the set
 * Parameters:
 *  set - a pointer to the current set
 *  object - a pointer to an object
 * Returns:
 *  A pointer to the current set OR
 *  NULL if the object does not exist in the set or
 *    if there is no object in the set
 */
ArraySet* ArraySet_RemoveObject(ArraySet* set, const Object *object);
/*
 * Description:
 *  Removes all objects from the set
 * Parameters:
 *  set - a pointer to the current set
 * Returns:
 *  A pointer to the set
 */
ArraySet* ArraySet_RemoveAll(ArraySet* set);
/*
 * Description:
 *  Creates a hard copy of a set
 * Parameters:
 *  set - a pointer to a set
 * Returns:
 *  A pointer to a copy of the set
 */
ArraySet* ArraySet_Copy(const ArraySet* set);
/*
 * Description:
 *  Compares two sets to see if they are equal
 * Parameters:
 *  set1, set2 - pointers to the two sets
 * Returns:
 *  True - if both sets have the same objects
 *  False - if there are differences between the sets
 */
bool ArraySet_Equals(const ArraySet* set1, const ArraySet* set2);
/*
 * Description:
 *  Check is a a set is a subset of another set
 * Parameters:
 *  smallSet - the possible subset
 *  largeSet - the set who is verified
 * Returns:
 *  True - if @smallset is a subset of @largeset
 *  False - otherwise
 */
bool ArraySet_IsSubset(const ArraySet* smallSet, const ArraySet* largeSet);
/*
 * Description:
 *  Creates a new ArraySet containing the result of the reunion
 *  between 2 other sets.
 * Parameters:
 *  set1, set2 - the sets who are going to be reunited
 * Returns:
 *  A pointer to the ArraySet containing the result of the reunion OR
 *  NULL if there is no memory available to allocate for the new set
 */
ArraySet* ArraySet_Reunion(const ArraySet* set1, const ArraySet* set2);
/*
 * Description:
 *  Creates a new ArraySet containing the result of the intersection
 *  between 2 other sets.
 * Parameters:
 *  set1, set2 - the sets who are going to be intersected
 * Returns:
 *  A pointer to the ArraySet containing the result of the intersection OR
 *  NULL if there is no memory available to allocate for the new set
 */
ArraySet* ArraySet_Intersection(const ArraySet* set1, const ArraySet* set2);
/*
 * Description:
 *  Creates a new ArraySet containing the result of the difference
 *  between the first set and the last set.
 * Parameters:
 *  mainSet - the set which will evaluated according to the secondary set
 *  secondarySet - the set containing the objects which will be eliminated
 *        from the main set.
 * Returns:
 *  A pointer to the ArraySet containing the result of set1 \ set2
 *  (the difference between set1 and set2) OR
 *  NULL if there is no memory available to allocate for the new set
 */
ArraySet* ArraySet_Difference(const ArraySet* mainSet, const ArraySet* secondarySet);
If you want to see the implementation details, follow this link:
In the examples below we shall consider the sample object defined in the following article:
Example 1
#include<stdio.h>
#include"Examples.h"
#include"ArraySet.h"
#include"SampleObject.h"

static void PrintArraySet(ArraySet* set)
{
   int i;
   printf("Size: %d Capacity: %d Objects: ", set->size, set->capacity);
   for (i = 0; i < set->size; i++)
   {
      printf("%d ", *(int*)(set->objects[i].data));
   }
   putchar('\n');
}

void ArraySet_Example1(void)
{
   /*We declare 2 ArraySet data structures*/
   ArraySet* s1 = NULL;
   ArraySet* s2 = NULL;
   /*The interface for the objects*/
   Interface* I_Integer = NULL;
   /*Creating the interface*/
   I_Integer = Interface_Create(&Integer_Copy,
                                &Integer_Destroy,
                                &Integer_Compare);
   /*Creating the first set*/
   s1 = ArraySet_Create(10);

   /*We shall add 6 objects who will symbolize the set {-3,5,2,0,-14,3}*/
   ArraySet_AddObject(s1, Object_Create(Integer_Create(-3),I_Integer));
   ArraySet_AddObject(s1, Object_Create(Integer_Create(5),I_Integer));
   ArraySet_AddObject(s1, Object_Create(Integer_Create(2),I_Integer));
   ArraySet_AddObject(s1, Object_Create(Integer_Create(0),I_Integer));
   ArraySet_AddObject(s1, Object_Create(Integer_Create(-14),I_Integer));
   ArraySet_AddObject(s1, Object_Create(Integer_Create(3),I_Integer));

   puts("After adding objects to s1: ");
   PrintArraySet(s1);

   /*Trying to add duplicate values*/
   ArraySet_AddObject(s1, Object_Create(Integer_Create(-3),I_Integer));
   ArraySet_AddObject(s1, Object_Create(Integer_Create(5),I_Integer));
   puts("After adding duplicate values to s1: ");
   PrintArraySet(s1);

   /*Creates a new ArraySet as a copy of the first ArraySet*/
   s2 = ArraySet_Copy(s1);

   /*Removing some objects from the first set*/
   ArraySet_RemoveObject(s1, Object_Create(Integer_Create(-3),I_Integer));
   ArraySet_RemoveObject(s1, Object_Create(Integer_Create(0),I_Integer));
   ArraySet_RemoveObject(s1, Object_Create(Integer_Create(-14),I_Integer));
   puts("After removing some objects from s1: ");
   PrintArraySet(s1);
   puts("s2 remains the same: ");
   PrintArraySet(s2);

   /*Resizing the array*/
   ArraySet_Resize(s1, 6);
   puts("After resizing s1 ");
   PrintArraySet(s1);

   /*After removing all objects from the second set*/
   puts("After removing all objects from s2: ");
   ArraySet_RemoveAll(s2);
   PrintArraySet(s2);
}
/*Output:
 After adding objects to s1:
 Size: 6 Capacity: 10 Objects: -3 5 2 0 -14 3
 After adding duplicate values to s1:
 Size: 6 Capacity: 10 Objects: -3 5 2 0 -14 3
 After removing some objects from s1:
 Size: 3 Capacity: 10 Objects: 5 2 3
 s2 remains the same:
 Size: 6 Capacity: 10 Objects: -3 5 2 0 -14 3
 After resizing s1
 Size: 3 Capacity: 6 Objects: 5 2 3
 After removing all objects from s2:
 Size: 0 Capacity: 10 Objects:
 */
Example 2
#include<stdio.h>
#include"Examples.h"
#include"ArraySet.h"
#include"SampleObject.h"

static void PrintArraySet(ArraySet* set)
{
   int i;
   printf("Size: %d Capacity: %d Objects: ", set->size, set->capacity);
   for (i = 0; i < set->size; i++)
   {
      printf("%d ", *(int*)(set->objects[i].data));
   }
   putchar('\n');
}

void ArraySet_Example2(void)
{
   ArraySet* s1 = NULL;
   ArraySet* s2 = NULL;
   ArraySet* r = NULL;
   Interface *I_Integer = NULL;
   bool result;

   /*Creating the Interface*/
   I_Integer = Interface_Create(&Integer_Copy,
                                &Integer_Destroy,
                                &Integer_Compare);

   /*Creating the first ArraySet and adding objects*/
   s1 = ArraySet_Create(10);
   ArraySet_AddObject(s1, Object_Create(Integer_Create(-3),I_Integer));
   ArraySet_AddObject(s1, Object_Create(Integer_Create(5),I_Integer));
   ArraySet_AddObject(s1, Object_Create(Integer_Create(2),I_Integer));
   ArraySet_AddObject(s1, Object_Create(Integer_Create(0),I_Integer));
   ArraySet_AddObject(s1, Object_Create(Integer_Create(-14),I_Integer));
   ArraySet_AddObject(s1, Object_Create(Integer_Create(3),I_Integer));

   /*Creates the second ArraySet and adding objects*/
   s2 = ArraySet_Create(10);
   ArraySet_AddObject(s2, Object_Create(Integer_Create(0),I_Integer));
   ArraySet_AddObject(s2, Object_Create(Integer_Create(1),I_Integer));
   ArraySet_AddObject(s2, Object_Create(Integer_Create(2),I_Integer));
   ArraySet_AddObject(s2, Object_Create(Integer_Create(0),I_Integer));
   ArraySet_AddObject(s2, Object_Create(Integer_Create(-14),I_Integer));
   ArraySet_AddObject(s2, Object_Create(Integer_Create(3),I_Integer));

   /*Prints the sets*/
   puts("Set S1");
   PrintArraySet(s1);
   puts("Set S2");
   PrintArraySet(s2);

   /*Checks if the set are equal*/
   result = ArraySet_Equals(s1, s2);
   if (result == true)
   {
      puts("S1 and S2 are equal");
   }
   else
   {
      puts("S1 and S2 are  not equal");
   }

   /*Checks if S1 is a subset of S2*/
   result = ArraySet_IsSubset(s1, s2);
   if (result == true)
   {
      puts("S1 is a subset of S2");
   }
   else
   {
      puts("S1 is not a subset of S2");
   }

   /*Computes the reunion of the two sets*/
   r = ArraySet_Reunion(s1, s2);
   puts("Reunion(S1,S2)");
   PrintArraySet(r);
   r = ArraySet_Destroy(r);

   /*Computes the intersection of the two sets*/
   puts("Intersection(S1,S2)");
   r = ArraySet_Intersection(s1, s2);
   PrintArraySet(r);
   r = ArraySet_Destroy(r);

   /*Computes the difference between S1 and S2*/
   r = ArraySet_Difference(s1, s2);
   puts("Difference(S1,S2)");
   PrintArraySet(r);
   r = ArraySet_Destroy(r);

   /*Computes the difference between S2 and S1*/
   r = ArraySet_Difference(s2, s1);
   puts("Difference(S2,S1");
   PrintArraySet(r);
   r = ArraySet_Destroy(r);
}
/*Output:
 Set S1
 Size: 6 Capacity: 10 Elements: -3 5 2 0 -14 3
 Set S2
 Size: 5 Capacity: 10 Elements: 0 1 2 -14 3
 S1 and S2 are  not equal
 S1 is not a subset of S2
 Reunion(S1,S2)
 Size: 7 Capacity: 11 Elements: -3 5 2 0 -14 3 1
 Intersection(S1,S2)
 Size: 4 Capacity: 10 Elements: 2 0 -14 3
 Difference(S1,S2)
 Size: 2 Capacity: 10 Elements: -3 5
 Difference(S2,S1
 Size: 1 Capacity: 10 Elements: 1
*/

Related Posts Plugin for WordPress, Blogger...